2

When I run an application in the IDE, accessing the parent directory (relative to the current directory) is easily done by using .. (two periods).

Here's a quick example:

Package Structure

src
└───main
    ├───java
    │   │   Main.java
    │   │
    │   └───controllers
    │           Controller.java
    │           InnerController.java
    │
    └───resources
        └───fxml
                inner-layout.fxml
                layout.fxml

From inside the Controller, I try to say, "Go up one level and then go find fxml/inner-layout.fxml".

URL relativeResource = Controller.class.getResource("../fxml/inner-layout.fxml");
// Returns: "file:/C:/Users/BradTurek/IdeaProjects/Parent%20Directory%20Jar%20MCVE/build/resources/main/fxml/inner-layout.fxml"

This works on the file system.

However, when I deploy the application as an executable .jar file, the .. is no longer interpreted as "go up one directory". I checked: I can still access resources in the .jar—except if I use a path including ...

It seems like .. just doesn't work when dealing with a path inside of a .jar.

Here's a full MCVE of the above example that you can download and run yourself.

The answer to this question isn't easily found on the internet—though it may be out there. My hope is that I've posed this question clearly enough to allow it's answer to be easily found by others.

Why does this happen?

Brad Turek
  • 2,472
  • 3
  • 30
  • 56
  • The answers at [How to get a path to a resource in a Java JAR file](https://stackoverflow.com/questions/941754/how-to-get-a-path-to-a-resource-in-a-java-jar-file) provide some of the information. – KevinO Apr 10 '18 at 04:11
  • @KevinO, thanks, but that doesn't quite answer the question as to why `..` doesn't work. I know I *could* use an absolute path, thank you. – Brad Turek Apr 10 '18 at 04:15
  • 3
    Because the javadoc doesn't say anywhere that using .. is supported, and there's thus no reason to assume it should be. – JB Nizet Apr 10 '18 at 06:02
  • Btw, there is no reason to use a relative path in your case. When using the class loading facilities to access resources, paths starting with "/" are always relative to the root of your class path, i.e. `"/fxml/inner-layout.fxml"` should work in your case. – isnot2bad Apr 10 '18 at 06:49
  • As per [the documentation](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getResource-java.lang.String-), resource paths **not** starting with a "/" are prepended with the (modified) package name. This means your resource path is interpreted as "/controllers/../fxml/inner-layout.fxml", which evidently works for file system, but not JAR. **Edit**: See answer by @isnot2bad – Itai Apr 10 '18 at 07:03
  • 1
    See also https://stackoverflow.com/questions/34755630/javafx-location-is-not-set-error – James_D Apr 10 '18 at 09:43

1 Answers1

4

First of all, it is to say, that the method Class.getResource(String) does not specify any support for handling ".." in the path. So the answer to your question could end here with the words: "Because the spec does not define this behaviour".

The reason why it works when the application is launched from inside the IDE, is, that in this case, the local file system facilities are used for accessing the resource. These are able to resolve the ".." in your path.

But a JAR file is not a file system by its own - it is just a compressed container holding all your .class files and other resources in a non-defined order (see JAR file specification - Oracle). Although it conforms to the ZIP file standard, which contains a directory listing at its end, called the 'central directory' (see ZIP (file format) - Wikipedia), one would have to use this listing first to build up a tree-like navigable path structure, before it could be used for resolving paths. I assume due to performance and memory consumption reasons this is not done for loading classes and other resources via class loader.

But in your case, there is really no reason to use relative paths at all and hence rely on path resolution. Instead, you can use absolute paths. This sounds worse as it is, because such absolute paths are not truly absolute in sense of your file system, but just absolute in relation to your class path root!

So this should work:

URL resource = Controller.class.getResource("/fxml/inner-layout.fxml");

It doesn't matter, whether the resources are loaded from your file system, from a JAR file or even via HTTP from a remote repository server.

isnot2bad
  • 24,105
  • 2
  • 29
  • 50
  • Thanks for explaining the difference in behavior between the local file system and the jar file system. It makes sense, *now*. – Brad Turek Apr 10 '18 at 13:53