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