8177530: Module system implementation refresh (4/2017)
Reviewed-by: mchung, alanb
Contributed-by: alan.bateman@oracle.com, mandy.chung@oracle.com
/*
* Copyright (c) 2016, 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
* @summary Basic test for redefineModule
*
* @build java.base/java.lang.TestProvider
* java.base/jdk.internal.test.TestProviderImpl1
* java.base/jdk.internal.test.TestProviderImpl2
* @run shell MakeJAR3.sh RedefineModuleAgent
* @run testng/othervm -javaagent:RedefineModuleAgent.jar RedefineModuleTest
*/
import java.lang.TestProvider;
import java.lang.instrument.Instrumentation;
import java.net.URLStreamHandler;
import java.net.spi.URLStreamHandlerProvider;
import java.nio.file.FileSystems;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@Test
public class RedefineModuleTest {
static void redefineModule(Module module,
Set<Module> extraReads,
Map<String, Set<Module>> extraExports,
Map<String, Set<Module>> extraOpens,
Set<Class<?>> extraUses,
Map<Class<?>, List<Class<?>>> extraProvides) {
RedefineModuleAgent.redefineModule(module,
extraReads,
extraExports,
extraOpens,
extraUses,
extraProvides);
}
static boolean isModifiableModule(Module module) {
return RedefineModuleAgent.isModifiableModule(module);
}
/**
* Use redefineModule to update java.base to read java.instrument
*/
public void testAddReads() {
Module baseModule = Object.class.getModule();
Module instrumentModule = Instrumentation.class.getModule();
// pre-conditions
assertFalse(baseModule.canRead(instrumentModule));
// update java.base to read java.instrument
Set<Module> extraReads = Set.of(instrumentModule);
redefineModule(baseModule, extraReads, Map.of(), Map.of(), Set.of(), Map.of());
assertTrue(baseModule.canRead(instrumentModule));
}
/**
* Use redefineModule to update java.base to export jdk.internal.misc
*/
public void testAddExports() {
Module baseModule = Object.class.getModule();
Module thisModule = this.getClass().getClassLoader().getUnnamedModule();
String pkg = "jdk.internal.misc";
// pre-conditions
assertFalse(baseModule.isExported(pkg));
assertFalse(baseModule.isExported(pkg, thisModule));
// update java.base to export jdk.internal.misc to an unnamed module
Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of(thisModule));
redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
assertFalse(baseModule.isExported(pkg));
assertTrue(baseModule.isExported(pkg, thisModule));
assertFalse(baseModule.isOpen(pkg));
assertFalse(baseModule.isOpen(pkg, thisModule));
}
/**
* Use redefineModule to update java.base to open jdk.internal.loader
*/
public void testAddOpens() {
Module baseModule = Object.class.getModule();
Module thisModule = this.getClass().getClassLoader().getUnnamedModule();
String pkg = "jdk.internal.loader";
// pre-conditions
assertFalse(baseModule.isOpen(pkg));
assertFalse(baseModule.isOpen(pkg, thisModule));
// update java.base to open dk.internal.loader to an unnamed module
Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of(thisModule));
redefineModule(baseModule, Set.of(), Map.of(), extraExports, Set.of(), Map.of());
assertFalse(baseModule.isExported(pkg));
assertTrue(baseModule.isExported(pkg, thisModule));
assertFalse(baseModule.isOpen(pkg));
assertTrue(baseModule.isOpen(pkg, thisModule));
}
/**
* Use redefineModule to update java.base to use TestProvider and
* provide implementations of TestProvider.
*/
public void testAddUsesAndProvides() throws Exception {
Module baseModule = Object.class.getModule();
Class<TestProvider> service = TestProvider.class;
// pre-conditions
assertFalse(baseModule.canUse(service));
assertTrue(collect(ServiceLoader.load(service)).isEmpty());
assertTrue(collect(ServiceLoader.load(ModuleLayer.boot(), service)).isEmpty());
// update java.base to use TestProvider
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(service), Map.of());
assertTrue(baseModule.canUse(service));
assertTrue(collect(ServiceLoader.load(service)).isEmpty());
assertTrue(collect(ServiceLoader.load(ModuleLayer.boot(), service)).isEmpty());
// update java.base to provide an implementation of TestProvider
Class<?> type1 = Class.forName("jdk.internal.test.TestProviderImpl1");
Map<Class<?>, List<Class<?>>> extraProvides = Map.of(service, List.of(type1));
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
// invoke ServiceLoader from java.base to find providers
Set<TestProvider> providers = collect(TestProvider.providers());
assertTrue(providers.size() == 1);
assertTrue(containsInstanceOf(providers, type1));
// use ServiceLoader to load implementations visible via TCCL
providers = collect(ServiceLoader.load(service));
assertTrue(collect(providers).size() == 1);
assertTrue(containsInstanceOf(collect(providers), type1));
// use ServiceLoader to load implementations in the boot layer
providers = collect(ServiceLoader.load(ModuleLayer.boot(), service));
assertTrue(collect(providers).size() == 1);
assertTrue(containsInstanceOf(collect(providers), type1));
// update java.base to provide a second implementation of TestProvider
Class<?> type2 = Class.forName("jdk.internal.test.TestProviderImpl2");
extraProvides = Map.of(service, List.of(type2));
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
// invoke ServiceLoader from java.base to find providers
providers = collect(TestProvider.providers());
assertTrue(providers.size() == 2);
assertTrue(containsInstanceOf(providers, type1));
assertTrue(containsInstanceOf(providers, type2));
// use ServiceLoader to load implementations visible via TCCL
providers = collect(ServiceLoader.load(service));
assertTrue(collect(providers).size() == 2);
assertTrue(containsInstanceOf(providers, type1));
assertTrue(containsInstanceOf(providers, type2));
// use ServiceLoader to load implementations in the boot layer
providers = collect(ServiceLoader.load(ModuleLayer.boot(), service));
assertTrue(collect(providers).size() == 2);
assertTrue(containsInstanceOf(providers, type1));
assertTrue(containsInstanceOf(providers, type2));
}
private <S> Set<S> collect(Iterable<S> sl) {
Set<S> providers = new HashSet<>();
sl.forEach(providers::add);
return providers;
}
private boolean containsInstanceOf(Collection<?> c, Class<?> type) {
for (Object o : c) {
if (type.isInstance(o)) return true;
}
return false;
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testExportPackageToEmptySet() {
// attempt to update java.base to export jdk.internal.misc to nobody
Module baseModule = Object.class.getModule();
Map<String, Set<Module>> extraExports = Map.of("jdk.internal.misc", Set.of());
redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOpenPackageToEmptySet() {
// attempt to update java.base to open jdk.internal.misc to nobody
Module baseModule = Object.class.getModule();
Map<String, Set<Module>> extraOpens = Map.of("jdk.internal.misc", Set.of());
redefineModule(baseModule, Set.of(), Map.of(), extraOpens, Set.of(), Map.of());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testProvideServiceWithEmptyList() throws Exception {
// update java.base to provide an empty list of TestProvider
Module baseModule = Object.class.getModule();
Class<?> service = TestProvider.class;
Map<Class<?>, List<Class<?>>> extraProvides = Map.of(service, List.of());
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
}
/**
* Test redefineClass by attempting to update java.base to export a package
* that it does not contain.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testExportPackageNotInModule() {
Module baseModule = Object.class.getModule();
String pkg = "jdk.doesnotexist";
// attempt to update java.base to export jdk.doesnotexist
Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of());
redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
}
/**
* Test redefineClass by attempting to update java.base to provide a service
* where the service provider class is not in the module.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testProvideServiceNotInModule() {
Module baseModule = Object.class.getModule();
class MyProvider extends URLStreamHandlerProvider {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
return null;
}
}
// attempt to update java.base to provide MyProvider
Map<Class<?>, List<Class<?>>> extraProvides
= Map.of(URLStreamHandlerProvider.class, List.of(MyProvider.class));
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
}
/**
* Test redefineClass by attempting to update java.base to provide a
* service where the service provider class is not a sub-type.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testProvideServiceNotSubtype() {
Module baseModule = Object.class.getModule();
Class<?> service = TestProvider.class;
Class<?> impl = FileSystems.getDefault().provider().getClass();
// attempt to update java.base to provide an implementation of TestProvider
Map<Class<?>, List<Class<?>>> extraProvides = Map.of(service, List.of(impl));
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
}
/**
* Exercise IsModifiableModule
*/
@Test
public void testIsModifiableModule() {
ClassLoader pcl = ClassLoader.getPlatformClassLoader();
ClassLoader scl = ClassLoader.getSystemClassLoader();
assertTrue(isModifiableModule(pcl.getUnnamedModule()));
assertTrue(isModifiableModule(scl.getUnnamedModule()));
assertTrue(isModifiableModule(RedefineModuleTest.class.getModule()));
assertTrue(isModifiableModule(Object.class.getModule()));
}
/**
* Test redefineClass with null
*/
public void testNulls() {
Module baseModule = Object.class.getModule();
try {
redefineModule(null, Set.of(), Map.of(), Map.of(), Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
redefineModule(baseModule, null, Map.of(), Map.of(), Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
redefineModule(baseModule, Set.of(), null, Map.of(), Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
redefineModule(baseModule, Set.of(), Map.of(), null, Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), null, Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), null);
assertTrue(false);
} catch (NullPointerException e) { }
try {
Set<Module> containsNull = new HashSet<>();
containsNull.add(null);
redefineModule(baseModule, containsNull, Map.of(), Map.of(), Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
Map<String, Set<Module>> extraExports = new HashMap<>();
extraExports.put(null, Set.of());
redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
Map<String, Set<Module>> extraExports = new HashMap<>();
extraExports.put(null, Set.of());
redefineModule(baseModule, Set.of(), Map.of(), extraExports, Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
Set<Module> containsNull = new HashSet<>();
containsNull.add(null);
Map<String, Set<Module>> extraExports = Map.of("java.lang", containsNull);
redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
Set<Class<?>> containsNull = new HashSet<>();
containsNull.add(null);
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), containsNull, Map.of());
assertTrue(false);
} catch (NullPointerException e) { }
try {
Map<Class<?>, List<Class<?>>> extraProvides = new HashMap<>();
extraProvides.put(null, List.of());
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
assertTrue(false);
} catch (NullPointerException e) { }
try {
List<Class<?>> containsNull = new ArrayList<>();
containsNull.add(null);
Map<Class<?>, List<Class<?>>> extraProvides = Map.of(TestProvider.class, containsNull);
redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
assertTrue(false);
} catch (NullPointerException e) { }
}
}