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 * @modules java.scripting 27 * @library modules /lib/testlibrary 28 * @build bananascript/* 29 * @build JarUtils 30 * @compile classpath/pearscript/org/pear/PearScriptEngineFactory.java 31 * classpath/pearscript/org/pear/PearScript.java 32 * @run testng/othervm ModulesTest 33 * @summary Basic test for ServiceLoader with a provider deployed as a module. 34 */ 35 36 import java.lang.module.Configuration; 37 import java.lang.module.ModuleFinder; 38 import java.nio.file.Files; 39 import java.nio.file.Path; 40 import java.nio.file.Paths; 41 import java.nio.file.StandardCopyOption; 42 import java.util.ArrayList; 43 import java.util.Collections; 44 import java.util.HashSet; 45 import java.util.Iterator; 46 import java.util.List; 47 import java.util.Optional; 48 import java.util.ServiceLoader; 49 import java.util.ServiceLoader.Provider; 50 import java.util.Set; 51 import java.util.stream.Collectors; 52 import javax.script.ScriptEngineFactory; 53 54 import org.testng.annotations.Test; 55 import org.testng.annotations.BeforeTest; 56 import static org.testng.Assert.*; 57 58 /** 59 * Basic test for ServiceLoader. The test make use of two service providers: 60 * 1. BananaScriptEngine - a ScriptEngineFactory deployed as a module on the 61 * module path. It implementations a singleton via the public static 62 * provider method. 63 * 2. PearScriptEngine - a ScriptEngineFactory deployed on the class path 64 * with a service configuration file. 65 */ 66 67 public class ModulesTest { 68 69 // Copy the services configuration file for "pearscript" into place. 70 @BeforeTest 71 public void setup() throws Exception { 72 Path src = Paths.get(System.getProperty("test.src")); 73 Path classes = Paths.get(System.getProperty("test.classes")); 74 String st = ScriptEngineFactory.class.getName(); 75 Path config = Paths.get("META-INF", "services", st); 76 Path source = src.resolve("classpath").resolve("pearscript").resolve(config); 77 Path target = classes.resolve(config); 78 Files.createDirectories(target.getParent()); 79 Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); 80 } 81 82 /** 83 * Basic test of iterator() to ensure that providers located as modules 84 * and on the class path are found. 85 */ 86 @Test 87 public void testIterator() { 88 ServiceLoader<ScriptEngineFactory> loader 89 = ServiceLoader.load(ScriptEngineFactory.class); 90 Set<String> names = collectAll(loader) 91 .stream() 92 .map(ScriptEngineFactory::getEngineName) 93 .collect(Collectors.toSet()); 94 assertTrue(names.contains("BananaScriptEngine")); 95 assertTrue(names.contains("PearScriptEngine")); 96 } 97 98 /** 99 * Basic test of iterator() to test iteration order. Providers deployed 100 * as named modules should be found before providers deployed on the class 101 * path. 102 */ 103 @Test 104 public void testIteratorOrder() { 105 ServiceLoader<ScriptEngineFactory> loader 106 = ServiceLoader.load(ScriptEngineFactory.class); 107 boolean foundUnnamed = false; 108 for (ScriptEngineFactory factory : collectAll(loader)) { 109 if (factory.getClass().getModule().isNamed()) { 110 if (foundUnnamed) { 111 assertTrue(false, "Named module element after unnamed"); 112 } 113 } else { 114 foundUnnamed = true; 115 } 116 } 117 } 118 119 /** 120 * Basic test of Provider::type 121 */ 122 @Test 123 public void testProviderType() { 124 Set<String> types = ServiceLoader.load(ScriptEngineFactory.class) 125 .stream() 126 .map(Provider::type) 127 .map(Class::getName) 128 .collect(Collectors.toSet()); 129 assertTrue(types.contains("org.banana.BananaScriptEngineFactory")); 130 assertTrue(types.contains("org.pear.PearScriptEngineFactory")); 131 } 132 133 /** 134 * Basic test of Provider::get 135 */ 136 @Test 137 public void testProviderGet() { 138 Set<String> names = ServiceLoader.load(ScriptEngineFactory.class) 139 .stream() 140 .map(Provider::get) 141 .map(ScriptEngineFactory::getEngineName) 142 .collect(Collectors.toSet()); 143 assertTrue(names.contains("BananaScriptEngine")); 144 assertTrue(names.contains("PearScriptEngine")); 145 } 146 147 /** 148 * Basic test of the public static provider method. BananaScriptEngine 149 * defines a provider method that returns the same instance. 150 */ 151 @Test 152 public void testSingleton() { 153 Optional<Provider<ScriptEngineFactory>> oprovider 154 = ServiceLoader.load(ScriptEngineFactory.class) 155 .stream() 156 .filter(p -> p.type().getName().equals("org.banana.BananaScriptEngineFactory")) 157 .findFirst(); 158 assertTrue(oprovider.isPresent()); 159 Provider<ScriptEngineFactory> provider = oprovider.get(); 160 161 // invoke Provider::get twice 162 ScriptEngineFactory factory1 = provider.get(); 163 ScriptEngineFactory factory2 = provider.get(); 164 assertTrue(factory1 == factory2); 165 } 166 167 /** 168 * Basic test of stream() to ensure that elements for providers in named 169 * modules come before elements for providers in unnamed modules. 170 */ 171 @Test 172 public void testStreamOrder() { 173 List<Class<?>> types = ServiceLoader.load(ScriptEngineFactory.class) 174 .stream() 175 .map(Provider::type) 176 .collect(Collectors.toList()); 177 178 boolean foundUnnamed = false; 179 for (Class<?> factoryClass : types) { 180 if (factoryClass.getModule().isNamed()) { 181 if (foundUnnamed) { 182 assertTrue(false, "Named module element after unnamed"); 183 } 184 } else { 185 foundUnnamed = true; 186 } 187 } 188 } 189 190 /** 191 * Basic test of ServiceLoader.findFirst() 192 */ 193 @Test 194 public void testFindFirst() { 195 Optional<ScriptEngineFactory> ofactory 196 = ServiceLoader.load(ScriptEngineFactory.class).findFirst(); 197 assertTrue(ofactory.isPresent()); 198 ScriptEngineFactory factory = ofactory.get(); 199 assertTrue(factory.getClass().getModule().isNamed()); 200 201 class S { } 202 assertFalse(ServiceLoader.load(S.class).findFirst().isPresent()); 203 } 204 205 /** 206 * Basic test ServiceLoader.load specifying the platform class loader. 207 * The providers on the module path and class path should not be located. 208 */ 209 @Test 210 public void testWithPlatformClassLoader() { 211 ClassLoader pcl = ClassLoader.getPlatformClassLoader(); 212 213 // iterator 214 ServiceLoader<ScriptEngineFactory> loader 215 = ServiceLoader.load(ScriptEngineFactory.class, pcl); 216 Set<String> names = collectAll(loader) 217 .stream() 218 .map(ScriptEngineFactory::getEngineName) 219 .collect(Collectors.toSet()); 220 assertFalse(names.contains("BananaScriptEngine")); 221 assertFalse(names.contains("PearScriptEngine")); 222 223 // stream 224 names = ServiceLoader.load(ScriptEngineFactory.class, pcl) 225 .stream() 226 .map(Provider::get) 227 .map(ScriptEngineFactory::getEngineName) 228 .collect(Collectors.toSet()); 229 assertFalse(names.contains("BananaScriptEngine")); 230 assertFalse(names.contains("PearScriptEngine")); 231 } 232 233 /** 234 * Basic test of ServiceLoader.load where the service provider module is an 235 * automatic module. 236 */ 237 @Test 238 public void testWithAutomaticModule() throws Exception { 239 Path classes = Paths.get(System.getProperty("test.classes")); 240 Path jar = Files.createTempDirectory("lib").resolve("pearscript.jar"); 241 JarUtils.createJarFile(jar, classes, "META-INF", "org"); 242 243 ModuleFinder finder = ModuleFinder.of(jar); 244 ModuleLayer bootLayer = ModuleLayer.boot(); 245 Configuration parent = bootLayer.configuration(); 246 Configuration cf = parent.resolveAndBind(finder, ModuleFinder.of(), Set.of()); 247 assertTrue(cf.modules().size() == 1); 248 249 ClassLoader scl = ClassLoader.getSystemClassLoader(); 250 ModuleLayer layer = bootLayer.defineModulesWithOneLoader(cf, scl); 251 assertTrue(layer.modules().size() == 1); 252 253 ClassLoader loader = layer.findLoader("pearscript"); 254 ScriptEngineFactory factory; 255 256 // load using the class loader as context 257 factory = ServiceLoader.load(ScriptEngineFactory.class, loader) 258 .findFirst() 259 .orElse(null); 260 assertNotNull(factory); 261 assertTrue(factory.getClass().getClassLoader() == loader); 262 263 // load using the layer as context 264 factory = ServiceLoader.load(layer, ScriptEngineFactory.class) 265 .findFirst() 266 .orElse(null); 267 assertNotNull(factory); 268 assertTrue(factory.getClass().getClassLoader() == loader); 269 } 270 271 /** 272 * Basic test of ServiceLoader.load, using the class loader for 273 * a module in a custom layer as the context. 274 */ 275 @Test 276 public void testWithCustomLayer1() { 277 ModuleLayer layer = createCustomLayer("bananascript"); 278 279 ClassLoader loader = layer.findLoader("bananascript"); 280 List<ScriptEngineFactory> providers 281 = collectAll(ServiceLoader.load(ScriptEngineFactory.class, loader)); 282 283 // should have at least 2 x bananascript + pearscript 284 assertTrue(providers.size() >= 3); 285 286 // first element should be the provider in the custom layer 287 ScriptEngineFactory factory = providers.get(0); 288 assertTrue(factory.getClass().getClassLoader() == loader); 289 assertTrue(factory.getClass().getModule().getLayer() == layer); 290 assertTrue(factory.getEngineName().equals("BananaScriptEngine")); 291 292 // remainder should be the boot layer 293 providers.remove(0); 294 Set<String> names = providers.stream() 295 .map(ScriptEngineFactory::getEngineName) 296 .collect(Collectors.toSet()); 297 assertTrue(names.contains("BananaScriptEngine")); 298 assertTrue(names.contains("PearScriptEngine")); 299 } 300 301 /** 302 * Basic test of ServiceLoader.load using a custom Layer as the context. 303 */ 304 @Test 305 public void testWithCustomLayer2() { 306 ModuleLayer layer = createCustomLayer("bananascript"); 307 308 List<ScriptEngineFactory> factories 309 = collectAll(ServiceLoader.load(layer, ScriptEngineFactory.class)); 310 311 // should have at least 2 x bananascript 312 assertTrue(factories.size() >= 2); 313 314 // first element should be the provider in the custom layer 315 ScriptEngineFactory factory = factories.get(0); 316 assertTrue(factory.getClass().getModule().getLayer() == layer); 317 assertTrue(factory.getEngineName().equals("BananaScriptEngine")); 318 319 // remainder should be the boot layer 320 factories.remove(0); 321 Set<String> names = factories.stream() 322 .map(ScriptEngineFactory::getEngineName) 323 .collect(Collectors.toSet()); 324 assertTrue(names.contains("BananaScriptEngine")); 325 assertFalse(names.contains("PearScriptEngine")); 326 } 327 328 /** 329 * Basic test of ServiceLoader.load with a tree of layers. 330 * 331 * Test scenario: 332 * - boot layer contains "bananascript", maybe other script engines 333 * - layer1, with boot layer as parent, contains "bananascript" 334 * - layer2, with boot layer as parent, contains "bananascript" 335 * - layer3, with layer1 ad layer as parents, contains "bananascript" 336 * 337 * ServiceLoader should locate all 4 script engine factories in DFS order. 338 */ 339 @Test 340 public void testWithCustomLayer3() { 341 ModuleLayer bootLayer = ModuleLayer.boot(); 342 Configuration cf0 = bootLayer.configuration(); 343 344 // boot layer should contain "bananascript" 345 List<ScriptEngineFactory> factories 346 = collectAll(ServiceLoader.load(bootLayer, ScriptEngineFactory.class)); 347 int countInBootLayer = factories.size(); 348 assertTrue(countInBootLayer >= 1); 349 assertTrue(factories.stream() 350 .map(p -> p.getEngineName()) 351 .filter("BananaScriptEngine"::equals) 352 .findAny() 353 .isPresent()); 354 355 ClassLoader scl = ClassLoader.getSystemClassLoader(); 356 Path dir = Paths.get(System.getProperty("test.classes", "."), "modules"); 357 ModuleFinder finder = ModuleFinder.of(dir); 358 359 // layer1 360 Configuration cf1 = cf0.resolveAndBind(finder, ModuleFinder.of(), Set.of()); 361 ModuleLayer layer1 = bootLayer.defineModulesWithOneLoader(cf1, scl); 362 assertTrue(layer1.modules().size() == 1); 363 364 // layer2 365 Configuration cf2 = cf0.resolveAndBind(finder, ModuleFinder.of(), Set.of()); 366 ModuleLayer layer2 = bootLayer.defineModulesWithOneLoader(cf2, scl); 367 assertTrue(layer2.modules().size() == 1); 368 369 // layer3 with layer1 and layer2 as parents 370 Configuration cf3 = Configuration.resolveAndBind(finder, 371 List.of(cf1, cf2), 372 ModuleFinder.of(), 373 Set.of()); 374 ModuleLayer layer3 375 = ModuleLayer.defineModulesWithOneLoader(cf3, List.of(layer1, layer2), scl).layer(); 376 assertTrue(layer3.modules().size() == 1); 377 378 379 // class loaders 380 ClassLoader loader1 = layer1.findLoader("bananascript"); 381 ClassLoader loader2 = layer2.findLoader("bananascript"); 382 ClassLoader loader3 = layer3.findLoader("bananascript"); 383 assertTrue(loader1 != loader2); 384 assertTrue(loader1 != loader3); 385 assertTrue(loader2 != loader3); 386 387 // load all factories with layer3 as the context 388 factories = collectAll(ServiceLoader.load(layer3, ScriptEngineFactory.class)); 389 int count = factories.size(); 390 assertTrue(count == countInBootLayer + 3); 391 392 // the ordering should be layer3, layer1, boot layer, layer2 393 394 ScriptEngineFactory factory = factories.get(0); 395 assertTrue(factory.getClass().getModule().getLayer() == layer3); 396 assertTrue(factory.getClass().getClassLoader() == loader3); 397 assertTrue(factory.getEngineName().equals("BananaScriptEngine")); 398 399 factory = factories.get(1); 400 assertTrue(factory.getClass().getModule().getLayer() == layer1); 401 assertTrue(factory.getClass().getClassLoader() == loader1); 402 assertTrue(factory.getEngineName().equals("BananaScriptEngine")); 403 404 // boot layer "bananascript" and maybe other factories 405 int last = count -1; 406 boolean found = false; 407 for (int i=2; i<last; i++) { 408 factory = factories.get(i); 409 assertTrue(factory.getClass().getModule().getLayer() == bootLayer); 410 if (factory.getEngineName().equals("BananaScriptEngine")) { 411 assertFalse(found); 412 found = true; 413 } 414 } 415 assertTrue(found); 416 417 factory = factories.get(last); 418 assertTrue(factory.getClass().getModule().getLayer() == layer2); 419 assertTrue(factory.getClass().getClassLoader() == loader2); 420 assertTrue(factory.getEngineName().equals("BananaScriptEngine")); 421 } 422 423 424 // -- nulls -- 425 426 @Test(expectedExceptions = { NullPointerException.class }) 427 public void testLoadNull1() { 428 ServiceLoader.load(null); 429 } 430 431 @Test(expectedExceptions = { NullPointerException.class }) 432 public void testLoadNull2() { 433 ServiceLoader.load((Class<?>) null, ClassLoader.getSystemClassLoader()); 434 } 435 436 @Test(expectedExceptions = { NullPointerException.class }) 437 public void testLoadNull3() { 438 class S { } 439 ServiceLoader.load((ModuleLayer) null, S.class); 440 } 441 442 @Test(expectedExceptions = { NullPointerException.class }) 443 public void testLoadNull4() { 444 ServiceLoader.load(ModuleLayer.empty(), null); 445 } 446 447 @Test(expectedExceptions = { NullPointerException.class }) 448 public void testLoadNull5() { 449 ServiceLoader.loadInstalled(null); 450 } 451 452 /** 453 * Create a custom layer by resolving the given module names. The modules 454 * are located in the {@code ${test.classes}/modules} directory. 455 */ 456 private ModuleLayer createCustomLayer(String... modules) { 457 Path dir = Paths.get(System.getProperty("test.classes", "."), "modules"); 458 ModuleFinder finder = ModuleFinder.of(dir); 459 Set<String> roots = new HashSet<>(); 460 Collections.addAll(roots, modules); 461 ModuleLayer bootLayer = ModuleLayer.boot(); 462 Configuration parent = bootLayer.configuration(); 463 Configuration cf = parent.resolve(finder, ModuleFinder.of(), roots); 464 ClassLoader scl = ClassLoader.getSystemClassLoader(); 465 ModuleLayer layer = bootLayer.defineModulesWithOneLoader(cf, scl); 466 assertTrue(layer.modules().size() == 1); 467 return layer; 468 } 469 470 private <E> List<E> collectAll(ServiceLoader<E> loader) { 471 List<E> list = new ArrayList<>(); 472 Iterator<E> iterator = loader.iterator(); 473 while (iterator.hasNext()) { 474 list.add(iterator.next()); 475 } 476 return list; 477 } 478 } 479