r/javahelp Feb 22 '22

Solved getResourceAsStream() == null when run from JAR

hello!

I have line of code such as this:

final Image roll_bg = new Image(this.getClass().getResourceAsStream("../resources/roll_bg.png"));

in IDE image is found and is rendered on canvas as expected, when exported to JAR, run ends with error. For JAR runs then, I did most hacky thing possible to extract what is wrong like so:

URL path = App.class.getResource("App.class");
if (path.toString().contains("jar")) {
    PrintStream ps = new PrintStream("./log.txt");
    System.setErr(ps);
    System.setOut(ps);
    System.out.println("Stream OUT: rerouted!");
    System.err.println("Stream ERR: rerouted!");
}

and log.txt says that InputStream is null (presumably did not found resource)

Caused by: java.lang.NullPointerException: Input stream must not be null
    at javafx.graphics/javafx.scene.image.Image.validateInputStream(Unknown Source)
    at javafx.graphics/javafx.scene.image.Image.<init>(Unknown Source)
    at com.engine.Engine.<init>(Engine.java:32)
    at com.engine.Engine.get(Engine.java:94)
    at com.App.start(App.java:60)

most online resources point to this method being able to reach resources from within JAR, does not work for me though. Thank you for any help offered!

6 Upvotes

24 comments sorted by

View all comments

2

u/dionthorn this.isAPro=false; this.helping=true; Feb 22 '22

Perhaps you could try

URL result = ClassName.class.getClassLoader().getResource("path/to/file");

JavaFX acts pretty strange in .jars sometimes. That is why we use JLink/JPackage now a days to distribute. JLink makes a custom JRE that only includes the parts you require for the module. JPackage creates native executables with the custom JRE so end users don't need to install Java or JavaFX or anything else.

https://docs.oracle.com/en/java/javase/14/docs/specs/man/jlink.html

https://docs.oracle.com/en/java/javase/14/docs/specs/man/jpackage.html

The JavaFX Maven Plugin is a great tool to automate using those tools to produce the distributable.

https://github.com/openjfx/javafx-maven-plugin

I have a JavaFX game project that uses the plugin and I distribute a ~28 mb MSI for end users that includes Java, JavaFX custom JRE.

https://github.com/dionthorn/LifeSimRPG

You might be interested in my FileOpUtil static class for handling File operations in JRT file systems:

https://github.com/dionthorn/LifeSimRPG/blob/main/src/main/java/org/dionthorn/lifesimrpg/FileOpUtil.java

2

u/sparkless12 Feb 22 '22

thank you for advice and links. As I mentioned, I have already tried accessing resources via Classname.class it did not solve the issue.

I will take a look at jlink and jpackage, I must say I am frustrated as hell to spend so much time on something relatively decent for my skill level only to find out that you actually cant even build the project... this gives hope :D .

I'll check out your game as well, maybe I will be able to take away some lessons that would take longer to learn if id have to bump into problems you have already went through.

1

u/dionthorn this.isAPro=false; this.helping=true; Feb 22 '22

I have another JavaFX game project that is more 2D oriented

https://github.com/dionthorn/2DTacticalRPG

This one is only JLink for now tho no fancy MSI installer uses a .bat file to easily run the custom JRE.

I do some fancy image manipulation for creating the tile sets

https://github.com/dionthorn/2DTacticalRPG/blob/master/src/main/java/org/dionthorn/tacticalrpg2d/render/TileSet.java

I use a very similar FileOpUtil

https://github.com/dionthorn/2DTacticalRPG/blob/master/src/main/java/org/dionthorn/tacticalrpg2d/data/FileOpUtil.java

This way I can create the tile set source Image like so:

Image tileSetSrc = new Image(FileOpUtil.GAME_ART_PATH + "/" + tileSetPath);

then I would cut them up with a method into an Image[]

Image[] makeTiles(Image tileSet, int TILE_SIZE)

2

u/sparkless12 Feb 22 '22

I see, I have pretty decent tile loading class myself which is probably 60% of why I would hate to loose the project I'm working on rn, but I am very interested in general structure and setup of any bigger/game oriented project in javaFX.

BTW I kinda fixed it pathsIt seems like Engine class calling resource with "../path/resources/image.png" is not okay, but App class calling "resources/image.png" is fine. It's only kinda fixed because I first need to understand why, but atleast it's not wasted project anymore.

1

u/dionthorn this.isAPro=false; this.helping=true; Feb 22 '22

Just the way they accept paths

getClass().getClassLoader().getResource("foo/bar.txt");

is the same call as

getClass().getResource("/foo/bar.txt");

https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getResource-java.lang.String-