1 /*
   2  * Copyright (c) 2015, 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  * @library /test/lib
  27  * @modules java.base/jdk.internal.module
  28  *          jdk.compiler
  29  *          jdk.jlink
  30  * @build ModuleReaderTest
  31  *        jdk.test.lib.compiler.CompilerUtils
  32  *        jdk.test.lib.util.JarUtils
  33  * @run testng ModuleReaderTest
  34  * @summary Basic tests for java.lang.module.ModuleReader
  35  */
  36 
  37 import java.io.File;
  38 import java.io.IOException;
  39 import java.io.InputStream;
  40 import java.lang.module.ModuleFinder;
  41 import java.lang.module.ModuleReader;
  42 import java.lang.module.ModuleReference;
  43 import java.net.URI;
  44 import java.net.URL;
  45 import java.net.URLConnection;
  46 import java.nio.ByteBuffer;
  47 import java.nio.file.Files;
  48 import java.nio.file.Path;
  49 import java.nio.file.Paths;
  50 import java.util.Arrays;
  51 import java.util.HashSet;
  52 import java.util.List;
  53 import java.util.Optional;
  54 import java.util.Set;
  55 import java.util.stream.Collectors;
  56 import java.util.spi.ToolProvider;
  57 
  58 import jdk.internal.module.ModulePath;
  59 import jdk.test.lib.compiler.CompilerUtils;
  60 import jdk.test.lib.util.JarUtils;
  61 
  62 import org.testng.annotations.BeforeTest;
  63 import org.testng.annotations.Test;
  64 import static org.testng.Assert.*;
  65 
  66 @Test
  67 public class ModuleReaderTest {
  68 
  69     private static final String TEST_SRC = System.getProperty("test.src");
  70 
  71     private static final Path USER_DIR   = Paths.get(System.getProperty("user.dir"));
  72     private static final Path SRC_DIR    = Paths.get(TEST_SRC, "src");
  73     private static final Path MODS_DIR   = Paths.get("mods");
  74 
  75     // the module name of the base module
  76     private static final String BASE_MODULE = "java.base";
  77 
  78     // the module name of the test module
  79     private static final String TEST_MODULE = "m";
  80 
  81     // resources in the base module
  82     private static final String[] BASE_RESOURCES = {
  83         "java/lang/Object.class"
  84     };
  85 
  86     // (directory) resources that may be in the base module
  87     private static final String[] MAYBE_BASE_RESOURCES = {
  88         "java",
  89         "java/",
  90         "java/lang",
  91         "java/lang/",
  92     };
  93 
  94     // resource names that should not be found in the base module
  95     private static final String[] NOT_BASE_RESOURCES = {
  96         "NotFound",
  97         "/java",
  98         "//java",
  99         "/java/lang",
 100         "//java/lang",
 101         "java//lang",
 102         "/java/lang/Object.class",
 103         "//java/lang/Object.class",
 104         "java/lang/Object.class/",
 105         "java//lang//Object.class",
 106         "./java/lang/Object.class",
 107         "java/./lang/Object.class",
 108         "java/lang/./Object.class",
 109         "../java/lang/Object.class",
 110         "java/../lang/Object.class",
 111         "java/lang/../Object.class",
 112     };
 113 
 114     // resources in test module (can't use module-info.class as a test
 115     // resource as it will be modified by the jmod tool)
 116     private static final String[] TEST_RESOURCES = {
 117         "p/Main.class"
 118     };
 119 
 120     // (directory) resources that may be in the test module
 121     private static final String[] MAYBE_TEST_RESOURCES = {
 122         "p",
 123         "p/"
 124     };
 125 
 126     // resource names that should not be found in the test module
 127     private static final String[] NOT_TEST_RESOURCES = {
 128         "NotFound",
 129         "/p",
 130         "//p",
 131         "/p/Main.class",
 132         "//p/Main.class",
 133         "p/Main.class/",
 134         "p//Main.class",
 135         "./p/Main.class",
 136         "p/./Main.class",
 137         "../p/Main.class",
 138         "p/../p/Main.class"
 139     };
 140 
 141 
 142     @BeforeTest
 143     public void compileTestModule() throws Exception {
 144 
 145         // javac -d mods/$TESTMODULE src/$TESTMODULE/**
 146         boolean compiled
 147             = CompilerUtils.compile(SRC_DIR.resolve(TEST_MODULE),
 148                                     MODS_DIR.resolve(TEST_MODULE));
 149         assertTrue(compiled, "test module did not compile");
 150     }
 151 
 152 
 153     /**
 154      * Test ModuleReader to module in runtime image
 155      */
 156     public void testImage() throws IOException {
 157 
 158         ModuleFinder finder = ModuleFinder.ofSystem();
 159         ModuleReference mref = finder.find(BASE_MODULE).get();
 160         ModuleReader reader = mref.open();
 161 
 162         try (reader) {
 163 
 164             for (String name : BASE_RESOURCES) {
 165                 byte[] expectedBytes;
 166                 Module baseModule = Object.class.getModule();
 167                 try (InputStream in = baseModule.getResourceAsStream(name)) {
 168                     expectedBytes = in.readAllBytes();
 169                 }
 170 
 171                 testFind(reader, name, expectedBytes);
 172                 testOpen(reader, name, expectedBytes);
 173                 testRead(reader, name, expectedBytes);
 174                 testList(reader, name);
 175             }
 176 
 177             // test resources that may be in the base module
 178             for (String name : MAYBE_BASE_RESOURCES) {
 179                 Optional<URI> ouri = reader.find(name);
 180                 ouri.ifPresent(uri -> {
 181                     if (name.endsWith("/"))
 182                         assertTrue(uri.toString().endsWith("/"));
 183                 });
 184             }
 185 
 186             // test "not found" in java.base module
 187             for (String name : NOT_BASE_RESOURCES) {
 188                 assertFalse(reader.find(name).isPresent());
 189                 assertFalse(reader.open(name).isPresent());
 190                 assertFalse(reader.read(name).isPresent());
 191             }
 192 
 193             // test nulls
 194             try {
 195                 reader.find(null);
 196                 assertTrue(false);
 197             } catch (NullPointerException expected) { }
 198 
 199             try {
 200                 reader.open(null);
 201                 assertTrue(false);
 202             } catch (NullPointerException expected) { }
 203 
 204             try {
 205                 reader.read(null);
 206                 assertTrue(false);
 207             } catch (NullPointerException expected) { }
 208 
 209             try {
 210                 reader.release(null);
 211                 assertTrue(false);
 212             } catch (NullPointerException expected) { }
 213 
 214         }
 215 
 216         // test closed ModuleReader
 217         try {
 218             reader.open(BASE_RESOURCES[0]);
 219             assertTrue(false);
 220         } catch (IOException expected) { }
 221 
 222 
 223         try {
 224             reader.read(BASE_RESOURCES[0]);
 225             assertTrue(false);
 226         } catch (IOException expected) { }
 227     }
 228 
 229 
 230     /**
 231      * Test ModuleReader to exploded module
 232      */
 233     public void testExplodedModule() throws IOException {
 234         test(MODS_DIR);
 235     }
 236 
 237 
 238     /**
 239      * Test ModuleReader to modular JAR
 240      */
 241     public void testModularJar() throws IOException {
 242         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
 243 
 244         // jar cf mlib/${TESTMODULE}.jar -C mods .
 245         JarUtils.createJarFile(dir.resolve("m.jar"),
 246                                MODS_DIR.resolve(TEST_MODULE));
 247 
 248         test(dir);
 249     }
 250 
 251 
 252     /**
 253      * Test ModuleReader to JMOD
 254      */
 255     public void testJMod() throws IOException {
 256         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
 257 
 258         // jmod create --class-path mods/${TESTMODULE}  mlib/${TESTMODULE}.jmod
 259         String cp = MODS_DIR.resolve(TEST_MODULE).toString();
 260         String jmod = dir.resolve("m.jmod").toString();
 261         String[] args = { "create", "--class-path", cp, jmod };
 262         ToolProvider jmodTool = ToolProvider.findFirst("jmod")
 263             .orElseThrow(() ->
 264                 new RuntimeException("jmod tool not found")
 265             );
 266         assertEquals(jmodTool.run(System.out, System.out, args), 0);
 267 
 268         test(dir);
 269     }
 270 
 271 
 272     /**
 273      * The test module is found on the given module path. Open a ModuleReader
 274      * to the test module and test the reader.
 275      */
 276     void test(Path mp) throws IOException {
 277 
 278         ModuleFinder finder = ModulePath.of(Runtime.version(), true, mp);
 279         ModuleReference mref = finder.find(TEST_MODULE).get();
 280         ModuleReader reader = mref.open();
 281 
 282         try (reader) {
 283 
 284             // test resources in test module
 285             for (String name : TEST_RESOURCES) {
 286                 byte[] expectedBytes
 287                     = Files.readAllBytes(MODS_DIR
 288                         .resolve(TEST_MODULE)
 289                         .resolve(name.replace('/', File.separatorChar)));
 290 
 291                 testFind(reader, name, expectedBytes);
 292                 testOpen(reader, name, expectedBytes);
 293                 testRead(reader, name, expectedBytes);
 294                 testList(reader, name);
 295             }
 296 
 297             // test resources that may be in the test module
 298             for (String name : MAYBE_TEST_RESOURCES) {
 299                 System.out.println(name);
 300                 Optional<URI> ouri = reader.find(name);
 301                 ouri.ifPresent(uri -> {
 302                     if (name.endsWith("/"))
 303                         assertTrue(uri.toString().endsWith("/"));
 304                 });
 305             }
 306 
 307             // test "not found" in test module
 308             for (String name : NOT_TEST_RESOURCES) {
 309                 assertFalse(reader.find(name).isPresent());
 310                 assertFalse(reader.open(name).isPresent());
 311                 assertFalse(reader.read(name).isPresent());
 312             }
 313 
 314             // test nulls
 315             try {
 316                 reader.find(null);
 317                 assertTrue(false);
 318             } catch (NullPointerException expected) { }
 319 
 320             try {
 321                 reader.open(null);
 322                 assertTrue(false);
 323             } catch (NullPointerException expected) { }
 324 
 325             try {
 326                 reader.read(null);
 327                 assertTrue(false);
 328             } catch (NullPointerException expected) { }
 329 
 330             try {
 331                 reader.release(null);
 332                 throw new RuntimeException();
 333             } catch (NullPointerException expected) { }
 334 
 335         }
 336 
 337         // test closed ModuleReader
 338         try {
 339             reader.open(TEST_RESOURCES[0]);
 340             assertTrue(false);
 341         } catch (IOException expected) { }
 342 
 343 
 344         try {
 345             reader.read(TEST_RESOURCES[0]);
 346             assertTrue(false);
 347         } catch (IOException expected) { }
 348 
 349         try {
 350             reader.list();
 351             assertTrue(false);
 352         } catch (IOException expected) { }
 353     }
 354 
 355     /**
 356      * Test ModuleReader#find
 357      */
 358     void testFind(ModuleReader reader, String name, byte[] expectedBytes)
 359         throws IOException
 360     {
 361         Optional<URI> ouri = reader.find(name);
 362         assertTrue(ouri.isPresent());
 363 
 364         URL url = ouri.get().toURL();
 365         if (!url.getProtocol().equalsIgnoreCase("jmod")) {
 366             URLConnection uc = url.openConnection();
 367             uc.setUseCaches(false);
 368             try (InputStream in = uc.getInputStream()) {
 369                 byte[] bytes = in.readAllBytes();
 370                 assertTrue(Arrays.equals(bytes, expectedBytes));
 371             }
 372         }
 373     }
 374 
 375     /**
 376      * Test ModuleReader#open
 377      */
 378     void testOpen(ModuleReader reader, String name, byte[] expectedBytes)
 379         throws IOException
 380     {
 381         Optional<InputStream> oin = reader.open(name);
 382         assertTrue(oin.isPresent());
 383 
 384         InputStream in = oin.get();
 385         try (in) {
 386             byte[] bytes = in.readAllBytes();
 387             assertTrue(Arrays.equals(bytes, expectedBytes));
 388         }
 389     }
 390 
 391     /**
 392      * Test ModuleReader#read
 393      */
 394     void testRead(ModuleReader reader, String name, byte[] expectedBytes)
 395         throws IOException
 396     {
 397         Optional<ByteBuffer> obb = reader.read(name);
 398         assertTrue(obb.isPresent());
 399 
 400         ByteBuffer bb = obb.get();
 401         try {
 402             int rem = bb.remaining();
 403             assertTrue(rem == expectedBytes.length);
 404             byte[] bytes = new byte[rem];
 405             bb.get(bytes);
 406             assertTrue(Arrays.equals(bytes, expectedBytes));
 407         } finally {
 408             reader.release(bb);
 409         }
 410     }
 411 
 412     /**
 413      * Test ModuleReader#list
 414      */
 415     void testList(ModuleReader reader, String name) throws IOException {
 416         List<String> list = reader.list().collect(Collectors.toList());
 417         Set<String> names = new HashSet<>(list);
 418         assertTrue(names.size() == list.size()); // no duplicates
 419 
 420         assertTrue(names.contains("module-info.class"));
 421         assertTrue(names.contains(name));
 422 
 423         // all resources should be locatable via find
 424         for (String e : names) {
 425             assertTrue(reader.find(e).isPresent());
 426         }
 427     }
 428 
 429 }