jdk/test/java/lang/instrument/RedefineModuleTest.java
author alanb
Fri, 07 Apr 2017 08:05:54 +0000
changeset 44545 83b611b88ac8
parent 44423 306c020eb154
child 45652 33342314ce89
permissions -rw-r--r--
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) { }
    }

}