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