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