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 * @summary Basic test for redefineModule
27 *
28 * @build java.base/java.lang.TestProvider
29 *        java.base/jdk.internal.test.TestProviderImpl1
30 *        java.base/jdk.internal.test.TestProviderImpl2
31 * @run shell MakeJAR3.sh RedefineModuleAgent
32 * @run testng/othervm --illegal-access=deny -javaagent:RedefineModuleAgent.jar RedefineModuleTest
33 */
34
35import java.lang.TestProvider;
36import java.lang.instrument.Instrumentation;
37import java.net.URLStreamHandler;
38import java.net.spi.URLStreamHandlerProvider;
39import java.nio.file.FileSystems;
40import java.util.ArrayList;
41import java.util.Collection;
42import java.util.HashMap;
43import java.util.HashSet;
44import java.util.List;
45import java.util.Map;
46import java.util.ServiceLoader;
47import java.util.Set;
48
49import org.testng.annotations.Test;
50import static org.testng.Assert.*;
51
52@Test
53public class RedefineModuleTest {
54
55    static void redefineModule(Module module,
56                               Set<Module> extraReads,
57                               Map<String, Set<Module>> extraExports,
58                               Map<String, Set<Module>> extraOpens,
59                               Set<Class<?>> extraUses,
60                               Map<Class<?>, List<Class<?>>> extraProvides) {
61        RedefineModuleAgent.redefineModule(module,
62                                           extraReads,
63                                           extraExports,
64                                           extraOpens,
65                                           extraUses,
66                                           extraProvides);
67    }
68
69    static boolean isModifiableModule(Module module) {
70        return RedefineModuleAgent.isModifiableModule(module);
71    }
72
73
74    /**
75     * Use redefineModule to update java.base to read java.instrument
76     */
77    public void testAddReads() {
78        Module baseModule = Object.class.getModule();
79        Module instrumentModule = Instrumentation.class.getModule();
80
81        // pre-conditions
82        assertFalse(baseModule.canRead(instrumentModule));
83
84        // update java.base to read java.instrument
85        Set<Module> extraReads = Set.of(instrumentModule);
86        redefineModule(baseModule, extraReads, Map.of(), Map.of(), Set.of(), Map.of());
87        assertTrue(baseModule.canRead(instrumentModule));
88    }
89
90    /**
91     * Use redefineModule to update java.base to export jdk.internal.misc
92     */
93    public void testAddExports() {
94        Module baseModule = Object.class.getModule();
95        Module thisModule = this.getClass().getClassLoader().getUnnamedModule();
96        String pkg = "jdk.internal.misc";
97
98        // pre-conditions
99        assertFalse(baseModule.isExported(pkg));
100        assertFalse(baseModule.isExported(pkg, thisModule));
101
102        // update java.base to export jdk.internal.misc to an unnamed module
103        Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of(thisModule));
104        redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
105        assertFalse(baseModule.isExported(pkg));
106        assertTrue(baseModule.isExported(pkg, thisModule));
107        assertFalse(baseModule.isOpen(pkg));
108        assertFalse(baseModule.isOpen(pkg, thisModule));
109    }
110
111    /**
112     * Use redefineModule to update java.base to open jdk.internal.loader
113     */
114    public void testAddOpens() {
115        Module baseModule = Object.class.getModule();
116        Module thisModule = this.getClass().getClassLoader().getUnnamedModule();
117        String pkg = "jdk.internal.loader";
118
119        // pre-conditions
120        assertFalse(baseModule.isOpen(pkg));
121        assertFalse(baseModule.isOpen(pkg, thisModule));
122
123        // update java.base to open dk.internal.loader to an unnamed module
124        Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of(thisModule));
125        redefineModule(baseModule, Set.of(), Map.of(), extraExports, Set.of(), Map.of());
126        assertFalse(baseModule.isExported(pkg));
127        assertTrue(baseModule.isExported(pkg, thisModule));
128        assertFalse(baseModule.isOpen(pkg));
129        assertTrue(baseModule.isOpen(pkg, thisModule));
130    }
131
132    /**
133     * Use redefineModule to update java.base to use TestProvider and
134     * provide implementations of TestProvider.
135     */
136    public void testAddUsesAndProvides() throws Exception {
137        Module baseModule = Object.class.getModule();
138        Class<TestProvider> service = TestProvider.class;
139
140        // pre-conditions
141        assertFalse(baseModule.canUse(service));
142        assertTrue(collect(ServiceLoader.load(service)).isEmpty());
143        assertTrue(collect(ServiceLoader.load(ModuleLayer.boot(), service)).isEmpty());
144
145        // update java.base to use TestProvider
146        redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(service), Map.of());
147        assertTrue(baseModule.canUse(service));
148        assertTrue(collect(ServiceLoader.load(service)).isEmpty());
149        assertTrue(collect(ServiceLoader.load(ModuleLayer.boot(), service)).isEmpty());
150
151        // update java.base to provide an implementation of TestProvider
152        Class<?> type1 = Class.forName("jdk.internal.test.TestProviderImpl1");
153        Map<Class<?>, List<Class<?>>> extraProvides = Map.of(service, List.of(type1));
154        redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
155
156        // invoke ServiceLoader from java.base to find providers
157        Set<TestProvider> providers = collect(TestProvider.providers());
158        assertTrue(providers.size() == 1);
159        assertTrue(containsInstanceOf(providers, type1));
160
161        // use ServiceLoader to load implementations visible via TCCL
162        providers = collect(ServiceLoader.load(service));
163        assertTrue(collect(providers).size() == 1);
164        assertTrue(containsInstanceOf(collect(providers), type1));
165
166        // use ServiceLoader to load implementations in the boot layer
167        providers = collect(ServiceLoader.load(ModuleLayer.boot(), service));
168        assertTrue(collect(providers).size() == 1);
169        assertTrue(containsInstanceOf(collect(providers), type1));
170
171        // update java.base to provide a second implementation of TestProvider
172        Class<?> type2 = Class.forName("jdk.internal.test.TestProviderImpl2");
173        extraProvides = Map.of(service, List.of(type2));
174        redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
175
176        // invoke ServiceLoader from java.base to find providers
177        providers = collect(TestProvider.providers());
178        assertTrue(providers.size() == 2);
179        assertTrue(containsInstanceOf(providers, type1));
180        assertTrue(containsInstanceOf(providers, type2));
181
182        // use ServiceLoader to load implementations visible via TCCL
183        providers = collect(ServiceLoader.load(service));
184        assertTrue(collect(providers).size() == 2);
185        assertTrue(containsInstanceOf(providers, type1));
186        assertTrue(containsInstanceOf(providers, type2));
187
188        // use ServiceLoader to load implementations in the boot layer
189        providers = collect(ServiceLoader.load(ModuleLayer.boot(), service));
190        assertTrue(collect(providers).size() == 2);
191        assertTrue(containsInstanceOf(providers, type1));
192        assertTrue(containsInstanceOf(providers, type2));
193    }
194
195    private <S> Set<S> collect(Iterable<S> sl) {
196        Set<S> providers = new HashSet<>();
197        sl.forEach(providers::add);
198        return providers;
199    }
200
201    private boolean containsInstanceOf(Collection<?> c, Class<?> type) {
202        for (Object o : c) {
203            if (type.isInstance(o)) return true;
204        }
205        return false;
206    }
207
208    @Test(expectedExceptions = IllegalArgumentException.class)
209    public void testExportPackageToEmptySet() {
210        // attempt to update java.base to export jdk.internal.misc to nobody
211        Module baseModule = Object.class.getModule();
212        Map<String, Set<Module>> extraExports = Map.of("jdk.internal.misc", Set.of());
213        redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
214    }
215
216    @Test(expectedExceptions = IllegalArgumentException.class)
217    public void testOpenPackageToEmptySet() {
218        // attempt to update java.base to open jdk.internal.misc to nobody
219        Module baseModule = Object.class.getModule();
220        Map<String, Set<Module>> extraOpens = Map.of("jdk.internal.misc", Set.of());
221        redefineModule(baseModule, Set.of(), Map.of(), extraOpens, Set.of(), Map.of());
222    }
223
224    @Test(expectedExceptions = IllegalArgumentException.class)
225    public void testProvideServiceWithEmptyList() throws Exception {
226        // update java.base to provide an empty list of TestProvider
227        Module baseModule = Object.class.getModule();
228        Class<?> service = TestProvider.class;
229        Map<Class<?>, List<Class<?>>> extraProvides = Map.of(service, List.of());
230        redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
231    }
232
233    /**
234     * Test redefineClass by attempting to update java.base to export a package
235     * that it does not contain.
236     */
237    @Test(expectedExceptions = IllegalArgumentException.class)
238    public void testExportPackageNotInModule() {
239        Module baseModule = Object.class.getModule();
240        String pkg = "jdk.doesnotexist";
241
242        // attempt to update java.base to export jdk.doesnotexist
243        Map<String, Set<Module>> extraExports = Map.of(pkg, Set.of());
244        redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
245    }
246
247    /**
248     * Test redefineClass by attempting to update java.base to provide a service
249     * where the service provider class is not in the module.
250     */
251    @Test(expectedExceptions = IllegalArgumentException.class)
252    public void testProvideServiceNotInModule() {
253        Module baseModule = Object.class.getModule();
254        class MyProvider extends URLStreamHandlerProvider {
255            @Override
256            public URLStreamHandler createURLStreamHandler(String protocol) {
257                return null;
258            }
259        }
260
261        // attempt to update java.base to provide MyProvider
262        Map<Class<?>, List<Class<?>>> extraProvides
263            = Map.of(URLStreamHandlerProvider.class, List.of(MyProvider.class));
264        redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
265    }
266
267    /**
268     * Test redefineClass by attempting to update java.base to provide a
269     * service where the service provider class is not a sub-type.
270     */
271    @Test(expectedExceptions = IllegalArgumentException.class)
272    public void testProvideServiceNotSubtype() {
273        Module baseModule = Object.class.getModule();
274
275        Class<?> service = TestProvider.class;
276        Class<?> impl = FileSystems.getDefault().provider().getClass();
277
278        // attempt to update java.base to provide an implementation of TestProvider
279        Map<Class<?>, List<Class<?>>> extraProvides = Map.of(service, List.of(impl));
280        redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
281    }
282
283    /**
284     * Exercise IsModifiableModule
285     */
286    @Test
287    public void testIsModifiableModule() {
288        ClassLoader pcl = ClassLoader.getPlatformClassLoader();
289        ClassLoader scl = ClassLoader.getSystemClassLoader();
290        assertTrue(isModifiableModule(pcl.getUnnamedModule()));
291        assertTrue(isModifiableModule(scl.getUnnamedModule()));
292        assertTrue(isModifiableModule(RedefineModuleTest.class.getModule()));
293        assertTrue(isModifiableModule(Object.class.getModule()));
294    }
295
296    /**
297     * Test redefineClass with null
298     */
299    public void testNulls() {
300        Module baseModule = Object.class.getModule();
301
302        try {
303            redefineModule(null, Set.of(), Map.of(), Map.of(), Set.of(), Map.of());
304            assertTrue(false);
305        } catch (NullPointerException e) { }
306
307        try {
308            redefineModule(baseModule, null, Map.of(), Map.of(), Set.of(), Map.of());
309            assertTrue(false);
310        } catch (NullPointerException e) { }
311
312        try {
313            redefineModule(baseModule, Set.of(), null, Map.of(), Set.of(), Map.of());
314            assertTrue(false);
315        } catch (NullPointerException e) { }
316
317        try {
318            redefineModule(baseModule, Set.of(), Map.of(), null, Set.of(), Map.of());
319            assertTrue(false);
320        } catch (NullPointerException e) { }
321
322        try {
323            redefineModule(baseModule, Set.of(), Map.of(), Map.of(), null, Map.of());
324            assertTrue(false);
325        } catch (NullPointerException e) { }
326
327        try {
328            redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), null);
329            assertTrue(false);
330        } catch (NullPointerException e) { }
331
332        try {
333            Set<Module> containsNull = new HashSet<>();
334            containsNull.add(null);
335            redefineModule(baseModule, containsNull, Map.of(), Map.of(), Set.of(), Map.of());
336            assertTrue(false);
337        } catch (NullPointerException e) { }
338
339        try {
340            Map<String, Set<Module>> extraExports = new HashMap<>();
341            extraExports.put(null, Set.of());
342            redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
343            assertTrue(false);
344        } catch (NullPointerException e) { }
345
346        try {
347            Map<String, Set<Module>> extraExports = new HashMap<>();
348            extraExports.put(null, Set.of());
349            redefineModule(baseModule, Set.of(), Map.of(), extraExports, Set.of(), Map.of());
350            assertTrue(false);
351        } catch (NullPointerException e) { }
352
353        try {
354            Set<Module> containsNull = new HashSet<>();
355            containsNull.add(null);
356            Map<String, Set<Module>> extraExports = Map.of("java.lang", containsNull);
357            redefineModule(baseModule, Set.of(), extraExports, Map.of(), Set.of(), Map.of());
358            assertTrue(false);
359        } catch (NullPointerException e) { }
360
361        try {
362            Set<Class<?>> containsNull = new HashSet<>();
363            containsNull.add(null);
364            redefineModule(baseModule, Set.of(), Map.of(), Map.of(), containsNull, Map.of());
365            assertTrue(false);
366        } catch (NullPointerException e) { }
367
368        try {
369            Map<Class<?>, List<Class<?>>> extraProvides = new HashMap<>();
370            extraProvides.put(null, List.of());
371            redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
372            assertTrue(false);
373        } catch (NullPointerException e) { }
374
375        try {
376            List<Class<?>> containsNull = new ArrayList<>();
377            containsNull.add(null);
378            Map<Class<?>, List<Class<?>>> extraProvides = Map.of(TestProvider.class, containsNull);
379            redefineModule(baseModule, Set.of(), Map.of(), Map.of(), Set.of(), extraProvides);
380            assertTrue(false);
381        } catch (NullPointerException e) { }
382    }
383
384}
385