mavenjavafxgetresource

ClassLoader.getResource("file") works but ClassLoader.getResource("folder/file") does not


In Intellij IDEA, I am trying to load resources in my Maven JavaFX modular project.

Here's my project's structure :

myProject
    - src
        - main
            - java
                - com
                    - example
                        - myPackage
                            - myApp.java
                - module-info.java
            - resources
                - fonts
                    - font.ttf
                - font.ttf

I duplicated the font I am trying to load in resources to test the following :

ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL rootFile = loader.getResource("font.ttf"); // this works
URL subfolderFile = loader.getResource("fonts/font.ttf"); // this does not work

System.out.println(rootFile); // file:/C:/Users/... stuff .../myProject/target/classes/font.ttf
System.out.println(subfolderFile); // null, was expecting file:/C:/Users/... stuff .../myProject/target/classes/fonts/font.ttf

I was unable to find any posts answering my problem. Does anyone have an explanation ?


Solution

  • Note the documentation of ClassLoader#getResource(String):

    Resources in named modules are subject to the encapsulation rules specified by Module.getResourceAsStream. Additionally, and except for the special case where the resource has a name ending with ".class", this method will only find resources in packages of named modules when the package is opened unconditionally [emphasis added] (even if the caller of this method is in the same module as the resource)

    You have a module-info.java file, so your code is in a named module. The fonts/font.ttf resource is in the fonts package, so it is encapsulated. Whereas the font.tff resource is at the root of the module, meaning it is not in a package, thus it is not encapsulated.

    If you add:

    opens fonts;
    

    To your module-info.java file then you should be able to load fonts/font.ttf via a ClassLoader.

    That said, it is often simpler to use Class#getResource(String). Particularly if the caller, the class, and the resource are all in the same module.