1

I have put all my JavaFX code into a executable JAR-file. Everything is inside there.

When I compile it with Eclipse IDE, it works, but when I try to execute the JAR-file, I get an error.

Caused by: javafx.fxml.LoadException: 
file:/home/asus/program/JUSBPlotter/JUSBPlotter-0.0.1-SNAPSHOT-jar-with-dependencies.jar!/se/danielmartensson/fxml/front.fxml

When I have this Java code

 Parent root = FXMLLoader.load(getClass().getResource("/se/danielmartensson/fxml/front.fxml"));

There is nothing wrong with the FXML file.

fx:controller="se.danielmartensson.controller.Front"

Also I get errors when I try to display a picture in JavaFX.

Caused by: java.io.FileNotFoundException: file:/home/asus/program/JUSBPlotter/JUSBPlotter-0.0.1-SNAPSHOT-jar-with-dependencies.jar!/se/danielmartensson/pictures/computer.png (No such file or directory)

When I have this Java code:

FileInputStream picture1 = new FileInputStream(getClass().getResource("/se/danielmartensson/pictures/computer.png").getFile());

Question: How can I tell Java the correct file path in JAR-file?

Naman
  • 27,789
  • 26
  • 218
  • 353
euraad
  • 2,467
  • 5
  • 30
  • 51
  • 1
    Make sure your fat jar file contains also the resources. It looks like it is missing them. Typically this can happen if you didn't run `mvn compile`, as the compile plugin calls also the resource plugin. – José Pereda May 30 '19 at 23:43
  • 4
    Question: If `.../computer.png` is a resource, why are you trying to read it with a `FileInputStream`? – Slaw May 30 '19 at 23:49
  • @JoséPereda well. I didn't call `mvn compile` becuase it won't work for me. I get errors like: `Main.java:[5,26] package javafx.application does not exist` – euraad May 31 '19 at 09:17
  • @Slaw because I don't know how to do else :) – euraad May 31 '19 at 09:18
  • Have a look at https://openjfx.io/openjfx-docs/#IDE-Intellij, Maven sections, and see how you can use the `javafx-maven-plugin` plugin, to compile and run your project. Once you get it working, try the shade/jar plugin to get your fat jar. – José Pereda May 31 '19 at 09:21
  • @JoséPereda oK! Compile is working now. But is there any way to create a fat JAR file and run it without modules? Like this `sudo java --module-path="/usr/share/openjfx/lib" --add-modules=javafx.controls,javafx.fxml -jar JUBSPlotter2-0.0.1-SNAPSHOT-jar-with-dependencies.jar ` – euraad May 31 '19 at 10:07
  • @JoséPereda I need to create a modular-project in maven? – euraad May 31 '19 at 10:10
  • A fat jar doesn't work with modules, everything is in the classpath. You can combine the javafx-maven-plugin to compile, and run, and the jar/shade plugin to create your fat jar. Also if you have a modular project (it is not a must), you can use jlink (from the javafx-maven plugin) to create a runtime image, instead of the fat jar. – José Pereda May 31 '19 at 10:19
  • @JoséPereda Ok! Now it was quite much information. I want to create an application that I only need to click on and then it starts. To do that I should not use the `maven-assembly-plugin`? Instead, i should use `mvn javafx:jlink` to create my executable image? – euraad May 31 '19 at 10:24
  • See this [answer](https://stackoverflow.com/a/55303408/3956070), and take your time to process it :) – José Pereda May 31 '19 at 10:26
  • @JoséPereda It looks like I have to follow the Modular manual from your openjfx URL link :) – euraad May 31 '19 at 10:41
  • @JoséPereda Hi! I got an error `Could not find goal 'jlink' in plugin org.openjfx:javafx-maven-plugin:0.0.1` Is it because I run OpenJDK 11? – euraad May 31 '19 at 10:55
  • Bump the plugin version to 0.0.2 – José Pereda May 31 '19 at 10:57
  • @JoséPereda Build success! That should be in the manual, because 0.0.1 is default. – euraad May 31 '19 at 11:00
  • That is in the manual, it shows JavaFX 12 and plugin 0.0.2, see for instance this [pic](https://openjfx.io/openjfx-docs/images/ide/intellij/maven/idea03.png). – José Pereda May 31 '19 at 11:04
  • @JoséPereda Ok. Thanks. When I run `mvn javafx:jlink` and my project is named `JUSBPlotter`, i'm expecting a folder named `JUSBPlotter` inside the `target` folder, but can't find any folders with the name `JUSBPlotter`. Or is it the `image` folder I should run? – euraad May 31 '19 at 11:07
  • @JoséPereda Now it's working. I just follow this manual to create an image! https://github.com/openjfx/javafx-maven-plugin JavaFX is great! – euraad May 31 '19 at 11:31

1 Answers1

2

One thing to remember is that resources are not the same thing as files, despite the fact they appear quite similar—especially before packaging an application. Once you package an application those resource files become entries in a JAR file which, while they can be thought of as files, cannot be accessed in the same way as files. In particular, the java.io.File API does not understand how to read those entries. You can, however, access these entries through the URL returned by Class#getResource(String) or the InputStream returned by Class#getResourceAsStream(String). Note the latter basically just finds the URL and, if it exists, calls URL#openStream(). The reason you can access JAR entries (i.e. resources) with the URL is due to the JarURLConnection and related classes; I won't go into detail about how URLs are handled by Java as that's out of scope for this answer.

Now, since you're trying to display an image in JavaFX I assume you're using the Image class. There are basically three ways you can tell an Image where the image resource is.

  1. Use Image#<init>(String) where the argument URL has no scheme. In this case the class will treat the URL as a path relative to the root of the classpath and try to access the image accordingly. Basically, the argument is the same argument you'd give to Class#getResource(String), except that the path must be absolute (i.e. not relative to any class). This behavior is documented by the Image class:

    All URLs supported by URL can be passed to the constructor. If the passed string is not a valid URL, but a path instead, the Image is searched on the classpath in that case.

    Note: If you're using modules then resource encapsulation comes into play when using this option. See GitHub Issue #441.

  2. Use the same constructor as used above but pass a valid URL (i.e. it has a scheme). When using a resource, you can simply convert the URL returned by Class#getResource(Sring) into a String using URL#toExternalForm().

    Image image = new Image(getClass().getResource(...).toExternalForm());
    

    Note: You can also use URL#toString() which returns the same value.

  3. Use Image#<init>(InputStream). Here you can use Class#getResourceAsStream(String). Don't forget to close the InputStream when done with it.

    try (InputStream is = getClass().getResourceAsStream(...)) {
        Image image = new Image(is);
    }
    

All this assumes that the resources have been properly packaged in your JAR file when you built it. If that's not the case, follow the advice given by José Pereda in the question comments first.


Note that one can access the entries of a ZIP/JAR file using the java.util.zip.* API, the java.util.jar.* API, or the Java NIO's ZipFileSystemProvider. Each of these, especially the last option, allow you to access the entries of a ZIP/JAR file similar to actual files because they're designed specifically to do so. However, when using resources in an application you should stick with Class#getResource(String) and related methods.

Slaw
  • 37,820
  • 8
  • 53
  • 80