1 /* 2 * Copyright (c) 2016, 2024, 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 * @library /test/lib 27 * @modules jdk.compiler 28 * @build jdk.test.lib.compiler.CompilerUtils 29 * @run testng/othervm BadProvidersTest 30 * @summary Basic test of ServiceLoader with bad provider and bad provider 31 * factories deployed on the module path 32 */ 33 34 import java.lang.classfile.ClassFile; 35 import java.lang.constant.ClassDesc; 36 import java.lang.constant.MethodTypeDesc; 37 import java.lang.module.Configuration; 38 import java.lang.module.ModuleFinder; 39 import java.lang.reflect.AccessFlag; 40 import java.nio.file.Files; 41 import java.nio.file.Path; 42 import java.nio.file.Paths; 43 import java.nio.file.StandardCopyOption; 44 import java.util.List; 45 import java.util.ServiceConfigurationError; 46 import java.util.ServiceLoader; 47 import java.util.ServiceLoader.Provider; 48 import java.util.Set; 49 import java.util.stream.Collectors; 50 51 import jdk.test.lib.compiler.CompilerUtils; 52 53 import org.testng.annotations.Test; 54 import org.testng.annotations.DataProvider; 55 56 import static java.lang.classfile.ClassFile.ACC_PUBLIC; 57 import static java.lang.classfile.ClassFile.ACC_STATIC; 58 import static java.lang.constant.ConstantDescs.CD_Object; 59 import static java.lang.constant.ConstantDescs.INIT_NAME; 60 import static java.lang.constant.ConstantDescs.MTD_void; 61 62 import static org.testng.Assert.*; 63 64 /** 65 * Basic test of `provides S with PF` and `provides S with P` where the provider 66 * factory or provider 67 */ 68 69 public class BadProvidersTest { 70 71 private static final String TEST_SRC = System.getProperty("test.src"); 72 73 private static final Path USER_DIR = Paths.get(System.getProperty("user.dir")); 74 private static final Path SRC_DIR = Paths.get(TEST_SRC, "modules"); 75 76 private static final Path BADFACTORIES_DIR = Paths.get(TEST_SRC, "badfactories"); 77 private static final Path BADPROVIDERS_DIR = Paths.get(TEST_SRC, "badproviders"); 78 79 private static final String TEST1_MODULE = "test1"; 80 private static final String TEST2_MODULE = "test2"; 81 82 private static final String TEST_SERVICE = "p.Service"; 83 84 /** 85 * Compiles a module, returning a module path with the compiled module. 86 */ 87 private Path compileTest(String moduleName) throws Exception { 88 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 89 Path output = Files.createDirectory(dir.resolve(moduleName)); 90 boolean compiled = CompilerUtils.compile(SRC_DIR.resolve(moduleName), output); 91 assertTrue(compiled); 92 return dir; 93 } 94 95 /** 96 * Resolves a test module and loads it into its own layer. ServiceLoader 97 * is then used to load all providers. 98 */ 99 private List<Provider> loadProviders(Path mp, String moduleName) throws Exception { 100 ModuleFinder finder = ModuleFinder.of(mp); 101 102 ModuleLayer bootLayer = ModuleLayer.boot(); 103 104 Configuration cf = bootLayer.configuration() 105 .resolveAndBind(finder, ModuleFinder.of(), Set.of(moduleName)); 106 107 ClassLoader scl = ClassLoader.getSystemClassLoader(); 108 109 ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl); 110 111 Class<?> service = layer.findLoader(moduleName).loadClass(TEST_SERVICE); 112 113 return ServiceLoader.load(layer, service) 114 .stream() 115 .collect(Collectors.toList()); 116 } 117 118 @Test 119 public void sanityTest1() throws Exception { 120 Path mods = compileTest(TEST1_MODULE); 121 List<Provider> list = loadProviders(mods, TEST1_MODULE); 122 assertTrue(list.size() == 1); 123 124 // the provider is a singleton, enforced by the provider factory 125 Object p1 = list.get(0).get(); 126 Object p2 = list.get(0).get(); 127 assertTrue(p1 != null); 128 assertTrue(p1 == p2); 129 } 130 131 @Test 132 public void sanityTest2() throws Exception { 133 Path mods = compileTest(TEST2_MODULE); 134 List<Provider> list = loadProviders(mods, TEST2_MODULE); 135 assertTrue(list.size() == 1); 136 Object p = list.get(0).get(); 137 assertTrue(p != null); 138 } 139 140 141 @DataProvider(name = "badfactories") 142 public Object[][] createBadFactories() { 143 return new Object[][] { 144 { "classnotpublic", null }, 145 { "methodnotpublic", null }, 146 { "badreturntype", null }, 147 { "returnsnull", null }, 148 { "throwsexception", null }, 149 }; 150 } 151 152 153 @Test(dataProvider = "badfactories", 154 expectedExceptions = ServiceConfigurationError.class) 155 public void testBadFactory(String testName, String ignore) throws Exception { 156 Path mods = compileTest(TEST1_MODULE); 157 158 // compile the bad factory 159 Path source = BADFACTORIES_DIR.resolve(testName); 160 Path output = Files.createTempDirectory(USER_DIR, "tmp"); 161 boolean compiled = CompilerUtils.compile(source, output); 162 assertTrue(compiled); 163 164 // copy the compiled class into the module 165 Path classFile = Paths.get("p", "ProviderFactory.class"); 166 Files.copy(output.resolve(classFile), 167 mods.resolve(TEST1_MODULE).resolve(classFile), 168 StandardCopyOption.REPLACE_EXISTING); 169 170 // load providers and instantiate each one 171 loadProviders(mods, TEST1_MODULE).forEach(Provider::get); 172 } 173 174 175 @DataProvider(name = "badproviders") 176 public Object[][] createBadProviders() { 177 return new Object[][] { 178 { "notpublic", null }, 179 { "ctornotpublic", null }, 180 { "notasubtype", null }, 181 { "throwsexception", null } 182 }; 183 } 184 185 186 @Test(dataProvider = "badproviders", 187 expectedExceptions = ServiceConfigurationError.class) 188 public void testBadProvider(String testName, String ignore) throws Exception { 189 Path mods = compileTest(TEST2_MODULE); 190 191 // compile the bad provider 192 Path source = BADPROVIDERS_DIR.resolve(testName); 193 Path output = Files.createTempDirectory(USER_DIR, "tmp"); 194 boolean compiled = CompilerUtils.compile(source, output); 195 assertTrue(compiled); 196 197 // copy the compiled class into the module 198 Path classFile = Paths.get("p", "Provider.class"); 199 Files.copy(output.resolve(classFile), 200 mods.resolve(TEST2_MODULE).resolve(classFile), 201 StandardCopyOption.REPLACE_EXISTING); 202 203 // load providers and instantiate each one 204 loadProviders(mods, TEST2_MODULE).forEach(Provider::get); 205 } 206 207 208 /** 209 * Test a service provider that defines more than one no-args 210 * public static "provider" method. 211 */ 212 @Test(expectedExceptions = ServiceConfigurationError.class) 213 public void testWithTwoFactoryMethods() throws Exception { 214 Path mods = compileTest(TEST1_MODULE); 215 216 var bytes = ClassFile.of().build(ClassDesc.of("p", "ProviderFactory"), clb -> { 217 clb.withSuperclass(CD_Object); 218 clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER); 219 220 var providerFactory$1 = ClassDesc.of("p", "ProviderFactory$1"); 221 222 // public static p.Service provider() 223 clb.withMethodBody("provider", MethodTypeDesc.of(ClassDesc.of("p", "Service")), 224 ACC_PUBLIC | ACC_STATIC, cob -> { 225 cob.new_(providerFactory$1); 226 cob.dup(); 227 cob.invokespecial(providerFactory$1, INIT_NAME, MTD_void); 228 cob.areturn(); 229 }); 230 231 // public static p.ProviderFactory$1 provider() 232 clb.withMethodBody("provider", MethodTypeDesc.of(providerFactory$1), 233 ACC_PUBLIC | ACC_STATIC, cob -> { 234 cob.new_(providerFactory$1); 235 cob.dup(); 236 cob.invokespecial(providerFactory$1, INIT_NAME, MTD_void); 237 cob.areturn(); 238 }); 239 }); 240 241 // write the class bytes into the compiled module directory 242 Path classFile = mods.resolve(TEST1_MODULE) 243 .resolve("p") 244 .resolve("ProviderFactory.class"); 245 Files.write(classFile, bytes); 246 247 // load providers and instantiate each one 248 loadProviders(mods, TEST1_MODULE).forEach(Provider::get); 249 } 250 251 } 252