Michael Sims
Michael Sims

Reputation: 2533

Cannot access Resource files in JavaFX 16 when module-info.java file exists

I just started using Java 16 and can't seem to figure out why I am unable to access resource files. I went through some troubleshooting to narrow down at least where I seem to be having a problem.

I'm using IntelliJ IDEA 2021.2 Build #IU-212.4746.92

I created a new project and I chose JavaFX with OpenJDK 16.

enter image description here

It then creates the project with three main files, an Application class, a Controller class, and a FXML file. Once I create the project, I go into the POM file and I chose version 16 of javaFX-controls and javafx-fxml and I tell it to get the latest version of the other libraries it adds automatically into the POM file.

I also copy two folders from a different project into the resources folder - all copy and pasting is done within IntelliJ.

When I run the application that it put there (called HellpApplication), it works fine. And that application uses class.getResource to grab the fxml file and again ... it works just fine. However, when I try to run this class:

import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;

public class FileResourcesUtils {

    public static void main(String[] args) throws URISyntaxException {
        FileResourcesUtils app = new FileResourcesUtils();
        String fileName = "StyleSheets/AnchorPane.css";
        System.out.println("getResourceAsStream : " + fileName);
        InputStream is = app.getFileFromResourceAsStream(fileName);
        printInputStream(is);
        System.out.println("\ngetResource : " + fileName);
        File file = app.getFileFromResource(fileName);
        printFile(file);
    }

    private InputStream getFileFromResourceAsStream(String fileName) {
        ClassLoader classLoader = getClass().getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream(fileName);
        if (inputStream == null) {
            throw new IllegalArgumentException("file not found! " + fileName);
        }
        else return inputStream;
    }

    private File getFileFromResource(String fileName) throws URISyntaxException{

        ClassLoader classLoader = getClass().getClassLoader();
        URL resource = classLoader.getResource(fileName);
        if (resource == null) {
            throw new IllegalArgumentException("file not found! " + fileName);
        }
        else return new File(resource.toURI());}

    private static void printInputStream(InputStream is) {
        try (InputStreamReader streamReader =
                     new InputStreamReader(is, StandardCharsets.UTF_8);
                     BufferedReader reader = new BufferedReader(streamReader)
        ) 
        {
            String line;
            while ((line = reader.readLine()) != null) System.out.println(line);
        } 
        catch (IOException e) {e.printStackTrace();}
    }

    private static void printFile(File file) {
        List<String> lines;
        try {
            lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
            lines.forEach(System.out::println);
        }
        catch (IOException e) {e.printStackTrace();}
    }

}

It throws this error:

getResourceAsStream : StyleSheets/AnchorPane.css
Exception in thread "main" java.lang.IllegalArgumentException: file not found! StyleSheets/AnchorPane.css
    at com.simtechdata.test/com.simtechdata.test.FileResourcesUtils.getFileFromResourceAsStream(FileResourcesUtils.java:27)
    at com.simtechdata.test/com.simtechdata.test.FileResourcesUtils.main(FileResourcesUtils.java:16)

Then, THE ONLY THING I HAVE TO DO ... is DELETE module-info.java, then that class runs perfectly! HOWEVER, without module-info.java there, I cannot run any FX code...

Here is module-info.java:

module com.simtechdata.test {
    requires javafx.controls;
    requires javafx.fxml;


    opens com.simtechdata.test to javafx.fxml;
    exports com.simtechdata.test;
}

Why did I use this class? Because I'm having the exact same problem trying to access resource files in my JavaFX application and this class was the easiest way for me to demonstrate the problem and show that it is specifically connected to the module-info file. When I run my own JavaFX code, it works just fine, until I try to access a resource file like a style sheet to throw onto a control.

What I would like to know is ... what sort of magic trick do I need to do in order to be able to access resource files from my JavaFX 16 application? What am I missing?

I'vr tried all of these different ways of getting to the resource file, but each one gives the same error:

String CSS_ANCHOR_PANE = this.getClass().getResource("StyleSheets/AnchorPane.css").toExternalForm();
String CSS_ANCHOR_PANE = ClassName.getClass().getResource("StyleSheets/AnchorPane.css").toExternalForm();

ClassLoader resource = ClassLoader.getSystemClassLoader();
String CSS_ANCHOR_PANE = resource.getResource("StyleSheets/AnchorPane.css").toExternalForm();

Class<?> resource = this.getClass();
String CSS_ANCHOR_PANE = resource.getResource("StyleSheets/AnchorPane.css").toExternalForm();

And here is a screenshot of the resource folder tree:

enter image description here

Any ideas?

Upvotes: 0

Views: 1569

Answers (2)

James_D
James_D

Reputation: 209674

This question is really answered in numerous other places around the StackOverflow site, but I'll pull together various solutions here.

You basically have three options:

  1. Use the getResource() (or getResourceAsStream(), though the former is slightly preferable) method defined in Class, instead of in ClassLoader with the correct path. This avoids any issues with modularity.
  2. Make the project non-modular (i.e. don't define a module-info.java file) and specify the modules to be added as runtime parameters to the JVM. This allows you to use either ClassLoader's getResource() or Class's getResource() method.
  3. Create a modular project and use ClassLoader's getResouce() method, and open the package containing the resource unconditionally.

Solution 1 is detailed here. Since the style sheet you are loading is in the StyleSheets package1, and the class you are loading it from appears (from the stack trace) to be in an unrelated package com.simtechdata.test, you should provide an absolute path:

String CSS_ANCHOR_PANE = this.getClass().getResource("/StyleSheets/AnchorPane.css").toExternalForm();

For solution 2, use

String CSS_ANCHOR_PANE = this.getClass().getClassLoader().getResource("StyleSheets/AnchorPane.css").toExternalForm();

Remove the module-info.java file, and run the application with the JVM arguments

--module-path="/path/to/javafx/modules" --add-modules="javafx.controls,javafx.fxml"

(Replace /path/to/javafx/modules with the actual file system path to the JavaFX modules. The javafx.fxml module is only required in the add-modules parameter if you are using FXML.)


For solution 3, use

String CSS_ANCHOR_PANE = this.getClass().getClassLoader().getResource("StyleSheets/AnchorPane.css").toExternalForm();

Since your resource is in the StyleSheets package1 you need to open that package unconditionally. Add the line

opens StyleSheets ;

to your module-info.java file. See https://stackoverflow.com/a/48790851/2189127


(1) It's very strongly advised to follow standard naming conventions; I'm not even sure if solution 3 will work with a non-standard package name (package names should be all lower-case).

Upvotes: 5

mipa
mipa

Reputation: 10640

  1. You write a lot about JavaFX but instead you should concentrate on the real problem. As you have shown yourself via your test program this problem is a module system problem and not a JavaFX problem.

  2. You can get rid of this problem by just not using the module system. Just wrap your main method into something like this:

    class MyAppLauncher {public static void main(String[] args) {MyApp.main(args);}}

The assumption is that MyApp is your main class which extends Application. The use MyAppLauncher to start your JavaFX program and remove all module system related options.

Upvotes: 1

Related Questions