/* * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @library /lib/testlibrary /test/lib * @modules jdk.compiler * @build LayerAndLoadersTest jdk.test.lib.compiler.CompilerUtils ModuleUtils * @run testng LayerAndLoadersTest * @summary Tests for java.lang.ModuleLayer@defineModulesWithXXX methods */ import java.io.IOException; import java.io.InputStream; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.lang.reflect.Method; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Collectors; import jdk.test.lib.compiler.CompilerUtils; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import static org.testng.Assert.*; @Test public class LayerAndLoadersTest { private static final String TEST_SRC = System.getProperty("test.src"); private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); private static final Path MODS_DIR = Paths.get("mods"); @BeforeTest public void setup() throws Exception { // javac -d mods --module-source-path src src/** assertTrue(CompilerUtils.compile(SRC_DIR, MODS_DIR, "--module-source-path", SRC_DIR.toString())); } /** * Basic test of ModuleLayer.defineModulesWithOneLoader * * Test scenario: * m1 requires m2 and m3 */ public void testWithOneLoader() throws Exception { Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl); checkLayer(layer, "m1", "m2", "m3"); ClassLoader cl1 = layer.findLoader("m1"); ClassLoader cl2 = layer.findLoader("m2"); ClassLoader cl3 = layer.findLoader("m3"); assertTrue(cl1.getParent() == scl); assertTrue(cl2 == cl1); assertTrue(cl3 == cl1); invoke(layer, "m1", "p.Main"); } /** * Basic test of ModuleLayer.defineModulesWithManyLoaders * * Test scenario: * m1 requires m2 and m3 */ public void testWithManyLoaders() throws Exception { Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl); checkLayer(layer, "m1", "m2", "m3"); ClassLoader cl1 = layer.findLoader("m1"); ClassLoader cl2 = layer.findLoader("m2"); ClassLoader cl3 = layer.findLoader("m3"); assertTrue(cl1.getParent() == scl); assertTrue(cl2.getParent() == scl); assertTrue(cl3.getParent() == scl); assertTrue(cl2 != cl1); assertTrue(cl3 != cl1); assertTrue(cl3 != cl2); invoke(layer, "m1", "p.Main"); } /** * Basic test of ModuleLayer.defineModulesWithOneLoader where one of the * modules is a service provider module. * * Test scenario: * m1 requires m2 and m3 * m1 uses S * m4 provides S with ... */ public void testServicesWithOneLoader() throws Exception { Configuration cf = resolveAndBind("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl); checkLayer(layer, "m1", "m2", "m3", "m4"); ClassLoader cl1 = layer.findLoader("m1"); ClassLoader cl2 = layer.findLoader("m2"); ClassLoader cl3 = layer.findLoader("m3"); ClassLoader cl4 = layer.findLoader("m4"); assertTrue(cl1.getParent() == scl); assertTrue(cl2 == cl1); assertTrue(cl3 == cl1); assertTrue(cl4 == cl1); Class serviceType = cl1.loadClass("p.Service"); assertTrue(serviceType.getClassLoader() == cl1); Iterator iter = ServiceLoader.load(serviceType, cl1).iterator(); Object provider = iter.next(); assertTrue(serviceType.isInstance(provider)); assertTrue(provider.getClass().getClassLoader() == cl1); assertFalse(iter.hasNext()); } /** * Basic test of ModuleLayer.defineModulesWithManyLoaders where one of the * modules is a service provider module. * * Test scenario: * m1 requires m2 and m3 * m1 uses S * m4 provides S with ... */ public void testServicesWithManyLoaders() throws Exception { Configuration cf = resolveAndBind("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl); checkLayer(layer, "m1", "m2", "m3", "m4"); ClassLoader cl1 = layer.findLoader("m1"); ClassLoader cl2 = layer.findLoader("m2"); ClassLoader cl3 = layer.findLoader("m3"); ClassLoader cl4 = layer.findLoader("m4"); assertTrue(cl1.getParent() == scl); assertTrue(cl2.getParent() == scl); assertTrue(cl3.getParent() == scl); assertTrue(cl4.getParent() == scl); assertTrue(cl2 != cl1); assertTrue(cl3 != cl1); assertTrue(cl3 != cl2); assertTrue(cl4 != cl1); assertTrue(cl4 != cl2); assertTrue(cl4 != cl3); Class serviceType = cl1.loadClass("p.Service"); assertTrue(serviceType.getClassLoader() == cl1); // Test that the service provider can be located via any of // the class loaders in the layer for (Module m : layer.modules()) { ClassLoader loader = m.getClassLoader(); Iterator iter = ServiceLoader.load(serviceType, loader).iterator(); Object provider = iter.next(); assertTrue(serviceType.isInstance(provider)); assertTrue(provider.getClass().getClassLoader() == cl4); assertFalse(iter.hasNext()); } } /** * Tests that the class loaders created by defineModulesWithXXX delegate * to the given parent class loader. */ public void testDelegationToParent() throws Exception { Configuration cf = resolve("m1"); ClassLoader parent = this.getClass().getClassLoader(); String cn = this.getClass().getName(); // one loader ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, parent); testLoad(layer, cn); // one loader with boot loader as parent layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, null); testLoadFail(layer, cn); // many loaders layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, parent); testLoad(layer, cn); // many loader with boot loader as parent layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, null); testLoadFail(layer, cn); } /** * Test defineModulesWithXXX when modules that have overlapping packages. * * Test scenario: * m1 exports p * m2 exports p */ public void testOverlappingPackages() { ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1").exports("p").build(); ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").exports("p").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); Configuration cf = ModuleLayer.boot() .configuration() .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2")); // cannot define both module m1 and m2 to the same class loader try { ModuleLayer.boot().defineModulesWithOneLoader(cf, null); assertTrue(false); } catch (LayerInstantiationException expected) { } // should be okay to have one module per class loader ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, null); checkLayer(layer, "m1", "m2"); } /** * Test ModuleLayer.defineModulesWithXXX with split delegation. * * Test scenario: * layer1: m1 exports p, m2 exports p * layer2: m3 reads m1, m4 reads m2 */ public void testSplitDelegation() { ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1").exports("p").build(); ModuleDescriptor descriptor2 = ModuleDescriptor.newModule("m2").exports("p").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); Configuration cf1 = ModuleLayer.boot() .configuration() .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2")); ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null); checkLayer(layer1, "m1", "m2"); ModuleDescriptor descriptor3 = ModuleDescriptor.newModule("m3").requires("m1").build(); ModuleDescriptor descriptor4 = ModuleDescriptor.newModule("m4").requires("m2").build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4); Configuration cf2 = cf1.resolve(finder2, ModuleFinder.of(), Set.of("m3", "m4")); // package p cannot be supplied by two class loaders try { layer1.defineModulesWithOneLoader(cf2, null); assertTrue(false); } catch (LayerInstantiationException expected) { } // no split delegation when modules have their own class loader ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null); checkLayer(layer2, "m3", "m4"); } /** * Test ModuleLayer.defineModulesWithXXX when the modules that override same * named modules in the parent layer. * * Test scenario: * layer1: m1, m2, m3 => same loader * layer2: m1, m2, m4 => same loader */ public void testOverriding1() throws Exception { Configuration cf1 = resolve("m1"); ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithOneLoader(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = ModuleFinder.of(MODS_DIR); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null); checkLayer(layer2, "m1", "m2", "m3"); invoke(layer1, "m1", "p.Main"); ClassLoader loader1 = layer1.findLoader("m1"); ClassLoader loader2 = layer1.findLoader("m2"); ClassLoader loader3 = layer1.findLoader("m3"); ClassLoader loader4 = layer2.findLoader("m1"); ClassLoader loader5 = layer2.findLoader("m2"); ClassLoader loader6 = layer2.findLoader("m3"); assertTrue(loader1 == loader2); assertTrue(loader1 == loader3); assertTrue(loader4 == loader5); assertTrue(loader4 == loader6); assertTrue(loader4 != loader1); assertTrue(loader1.loadClass("p.Main").getClassLoader() == loader1); assertTrue(loader1.loadClass("q.Hello").getClassLoader() == loader1); assertTrue(loader1.loadClass("w.Hello").getClassLoader() == loader1); assertTrue(loader4.loadClass("p.Main").getClassLoader() == loader4); assertTrue(loader4.loadClass("q.Hello").getClassLoader() == loader4); assertTrue(loader4.loadClass("w.Hello").getClassLoader() == loader4); } /** * Test Layer defineModulesWithXXX when the modules that override same * named modules in the parent layer. * * Test scenario: * layer1: m1, m2, m3 => loader pool * layer2: m1, m2, m3 => loader pool */ public void testOverriding2() throws Exception { Configuration cf1 = resolve("m1"); ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = ModuleFinder.of(MODS_DIR); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null); checkLayer(layer2, "m1", "m2", "m3"); invoke(layer1, "m1", "p.Main"); ClassLoader loader1 = layer1.findLoader("m1"); ClassLoader loader2 = layer1.findLoader("m2"); ClassLoader loader3 = layer1.findLoader("m3"); ClassLoader loader4 = layer2.findLoader("m1"); ClassLoader loader5 = layer2.findLoader("m2"); ClassLoader loader6 = layer2.findLoader("m3"); assertTrue(loader4 != loader1); assertTrue(loader5 != loader2); assertTrue(loader6 != loader3); assertTrue(loader1.loadClass("p.Main").getClassLoader() == loader1); assertTrue(loader1.loadClass("q.Hello").getClassLoader() == loader2); assertTrue(loader1.loadClass("w.Hello").getClassLoader() == loader3); // p.Main is not visible via loader2 try { loader2.loadClass("p.Main"); assertTrue(false); } catch (ClassNotFoundException expected) { } // w.Hello is not visible via loader2 try { loader2.loadClass("w.Hello"); assertTrue(false); } catch (ClassNotFoundException expected) { } // p.Main is not visible via loader3 try { loader3.loadClass("p.Main"); assertTrue(false); } catch (ClassNotFoundException expected) { } // q.Hello is not visible via loader3 try { loader3.loadClass("q.Hello"); assertTrue(false); } catch (ClassNotFoundException expected) { } assertTrue(loader4.loadClass("p.Main").getClassLoader() == loader4); assertTrue(loader5.loadClass("q.Hello").getClassLoader() == loader5); assertTrue(loader6.loadClass("w.Hello").getClassLoader() == loader6); // p.Main is not visible via loader5 try { loader5.loadClass("p.Main"); assertTrue(false); } catch (ClassNotFoundException expected) { } // w.Hello is not visible via loader5 try { loader5.loadClass("w.Hello"); assertTrue(false); } catch (ClassNotFoundException expected) { } // p.Main is not visible via loader6 try { loader6.loadClass("p.Main"); assertTrue(false); } catch (ClassNotFoundException expected) { } // q.Hello is not visible via loader6 try { loader6.loadClass("q.Hello"); assertTrue(false); } catch (ClassNotFoundException expected) { } } /** * Test ModuleLayer.defineModulesWithXXX when the modules that override same * named modules in the parent layer. * * layer1: m1, m2, m3 => same loader * layer2: m1, m3 => same loader */ public void testOverriding3() throws Exception { Configuration cf1 = resolve("m1"); ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithOneLoader(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = finderFor("m1", "m3"); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null); checkLayer(layer2, "m1", "m3"); invoke(layer1, "m1", "p.Main"); ClassLoader loader1 = layer1.findLoader("m1"); ClassLoader loader2 = layer2.findLoader("m1"); assertTrue(loader1.loadClass("p.Main").getClassLoader() == loader1); assertTrue(loader1.loadClass("q.Hello").getClassLoader() == loader1); assertTrue(loader1.loadClass("w.Hello").getClassLoader() == loader1); assertTrue(loader2.loadClass("p.Main").getClassLoader() == loader2); assertTrue(loader2.loadClass("q.Hello").getClassLoader() == loader1); assertTrue(loader2.loadClass("w.Hello").getClassLoader() == loader2); } /** * Test Layer defineModulesWithXXX when the modules that override same * named modules in the parent layer. * * layer1: m1, m2, m3 => loader pool * layer2: m1, m3 => loader pool */ public void testOverriding4() throws Exception { Configuration cf1 = resolve("m1"); ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null); checkLayer(layer1, "m1", "m2", "m3"); ModuleFinder finder = finderFor("m1", "m3"); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null); checkLayer(layer2, "m1", "m3"); invoke(layer1, "m1", "p.Main"); ClassLoader loader1 = layer1.findLoader("m1"); ClassLoader loader2 = layer1.findLoader("m2"); ClassLoader loader3 = layer1.findLoader("m3"); ClassLoader loader4 = layer2.findLoader("m1"); ClassLoader loader5 = layer2.findLoader("m2"); ClassLoader loader6 = layer2.findLoader("m3"); assertTrue(loader4 != loader1); assertTrue(loader5 == loader2); // m2 not overridden assertTrue(loader6 != loader3); assertTrue(loader1.loadClass("p.Main").getClassLoader() == loader1); assertTrue(loader1.loadClass("q.Hello").getClassLoader() == loader2); assertTrue(loader1.loadClass("w.Hello").getClassLoader() == loader3); assertTrue(loader2.loadClass("q.Hello").getClassLoader() == loader2); assertTrue(loader3.loadClass("w.Hello").getClassLoader() == loader3); assertTrue(loader4.loadClass("p.Main").getClassLoader() == loader4); assertTrue(loader4.loadClass("q.Hello").getClassLoader() == loader2); assertTrue(loader4.loadClass("w.Hello").getClassLoader() == loader6); assertTrue(loader6.loadClass("w.Hello").getClassLoader() == loader6); } /** * Basic test of resource loading with a class loader created by * Layer.defineModulesWithOneLoader. */ public void testResourcesOneLoader() throws Exception { Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl); ClassLoader loader = layer.findLoader("m1"); testResourceLoading(loader, "p/Main.class"); } /** * Basic test of resource loading with a class loader created by * Layer.defineModulesWithOneLoader. */ public void testResourcesManyLoaders() throws Exception { Configuration cf = resolve("m1"); ClassLoader scl = ClassLoader.getSystemClassLoader(); ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl); ClassLoader loader = layer.findLoader("m1"); testResourceLoading(loader, "p/Main.class"); } /** * Test that a resource is located by a class loader. */ private void testResourceLoading(ClassLoader loader, String name) throws IOException { URL url = loader.getResource(name); assertNotNull(url); try (InputStream in = loader.getResourceAsStream(name)) { assertNotNull(in); } Enumeration urls = loader.getResources(name); assertTrue(urls.hasMoreElements()); } // -- supporting methods -- /** * Resolve the given modules, by name, and returns the resulting * Configuration. */ private static Configuration resolve(String... roots) { ModuleFinder finder = ModuleFinder.of(MODS_DIR); return ModuleLayer.boot() .configuration() .resolve(finder, ModuleFinder.of(), Set.of(roots)); } /** * Resolve the given modules, by name, and returns the resulting * Configuration. */ private static Configuration resolveAndBind(String... roots) { ModuleFinder finder = ModuleFinder.of(MODS_DIR); return ModuleLayer.boot() .configuration() .resolveAndBind(finder, ModuleFinder.of(), Set.of(roots)); } /** * Invokes the static void main(String[]) method on the given class * in the given module. */ private static void invoke(ModuleLayer layer, String mn, String mc) throws Exception { ClassLoader loader = layer.findLoader(mn); Class c = loader.loadClass(mc); Method mainMethod = c.getMethod("main", String[].class); mainMethod.invoke(null, (Object)new String[0]); } /** * Checks that the given layer contains exactly the expected modules * (by name). */ private void checkLayer(ModuleLayer layer, String ... expected) { Set names = layer.modules().stream() .map(Module::getName) .collect(Collectors.toSet()); assertTrue(names.size() == expected.length); for (String name : expected) { assertTrue(names.contains(name)); } } /** * Test that a class can be loaded via the class loader of all modules * in the given layer. */ static void testLoad(ModuleLayer layer, String cn) throws Exception { for (Module m : layer.modules()) { ClassLoader l = m.getClassLoader(); l.loadClass(cn); } } /** * Test that a class cannot be loaded via any of the class loaders of * the modules in the given layer. */ static void testLoadFail(ModuleLayer layer, String cn) throws Exception { for (Module m : layer.modules()) { ClassLoader l = m.getClassLoader(); try { l.loadClass(cn); assertTrue(false); } catch (ClassNotFoundException expected) { } } } /** * Returns a ModuleFinder that only finds the given test modules */ static ModuleFinder finderFor(String... names) { ModuleFinder finder = ModuleFinder.of(MODS_DIR); Map mrefs = new HashMap<>(); for (String name : names) { Optional omref = finder.find(name); assert omref.isPresent(); mrefs.put(name, omref.get()); } return new ModuleFinder() { @Override public Optional find(String name) { ModuleReference mref = mrefs.get(name); return Optional.ofNullable(mref); } @Override public Set findAll() { return mrefs.values().stream().collect(Collectors.toSet()); } }; } }