e63023546a
Co-authored-by: Alan Bateman <alanb@openjdk.org> Reviewed-by: mchung, alanb, hseigel
385 lines
16 KiB
Java
385 lines
16 KiB
Java
/*
|
|
* Copyright (c) 2016, 2021, 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) { }
|
|
}
|
|
|
|
}
|