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