Search code examples
javajava-platform-module-systemmodule-info

How to access files in /META-INF/resources when module-info.java is present?


When TestNG invokes Class.getResourceAsStream() on an external module, it is returning null.

Per Resources visibility with java9 modules when a user wants to access /resources/someResource.xml in a library that contains module-info.java, the module must opens resources unconditionally.

Fair enough. But what happens if a user wants to access /META-INF/resources/someResource.xml in such a library? What opens instruction is necessary in this case?

UPDATE:

The bug report was filed against version 7.5 of TestNG. I just noticed that if we look at the source code of that version we see it is trying to load /META-INF/resources/webjars/jquery/3.5.1/jquery.min.js.


Solution

  • From my testing, opening the package is only necessary when the file is in the same package as a class in the module. In that case, you need to open the package of the class in the same module.

    So, assume you have a project like the following:

    |-A
    | |-module-info.java
    | |-a
    |   |-A.java
    |   |-x
    |     |-X
    |-B
      |-module-info.java
      |-b
        |-B.java
    

    and A/module-info.java:

    module a {
        exports a;
    }
    

    as well as B/module-info.java:

    module b {
        requires a;
    }
    

    then compile it with the following commands:

    cd A
    javac module-info.java a\A.java
    cd ..
    cd B
    javac --module-path ..\A module-info.java b\B.java
    cd ..
    

    The content of class A is irrelevant here (it just needs to exist and have a class/interface/enum/record/whatever declaration with the correct name).

    We then let B read the file a/x/X from module A:

    package b;
    
    import java.io.*;
    import a.A;
    
    public class B{
        public static void main(String[] args) throws Exception{
            try(BufferedReader br = new BufferedReader(new InputStreamReader(A.class.getClassLoader().getResourceAsStream("a/x/X")))){
                System.out.println(br.readLine());
            }
        }
    }
    

    When running it with

    java --module-path A;B -m b/b.B
    

    it displays the first line of a/x/X.

    However, we cannot access files in the same directory as the A.class file:

    package b;
    
    import java.io.*;
    import a.A;
    
    public class B{
        public static void main(String[] args) throws Exception{
            try(BufferedReader br = new BufferedReader(new InputStreamReader(A.class.getResourceAsStream("A.java")))){
                System.out.println(br.readLine());
            }
        }
    }
    

    If we now add opens a in A/module-info.java (and recompile the module with the above command), the resource can be read.

    If we want to read /META-INF/a/A.txt or similar (in the A module), no opens statement is required as there is no class in the same package.