I am migrating an application from Oracle Java 8 to openJDK11 + openJFX11. In the Java 8 version, I loaded a properties file using the following code (CryoStats is the class containing the code):
try {
jarLocation = CryoStats.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
confLocation = jarLocation.substring(0, jarLocation.lastIndexOf("/")) + "/config/";
try (FileInputStream input = new FileInputStream(confLocation + "CryoStats.properties");) {
properties.load(input);
HORUS_HOST = properties.getProperty("HORUS_HOST");
HORUS_USERNAME = properties.getProperty("HORUS_USERNAME");
HORUS_PASSWORD = properties.getProperty("HORUS_PASSWORD");
CONNECTION_TIMEOUT = Integer.parseInt(properties.getProperty("CONNECTION_TIMEOUT"));
SOCKET_TIMEOUT = Integer.parseInt(properties.getProperty("SOCKET_TIMEOUT"));
System.out.println("CryoStats.properties loaded.");
} catch (FileNotFoundException ex) {
System.out.println("Error loading properties file");
System.out.println(ex.getMessage());
} catch (IOException ex) {
System.out.println("Error loading properties file");
System.out.println(ex.getMessage());
}
} catch (URISyntaxException ex) {
System.out.println(ex.getMessage());
}
In my Netbeans build configuration, I had a task that copied the "config" folder to the "dist" folder, which therefore included: my jar file, a "lib" folder, a "config" folder. This code worked both within Netbeans and at run-time.
I need to give the user the possibility to edit the properties file, so I need to keep it outside of the jar.
I have now migrated the code to an Eclipse project, and I compile against openJDK11 and openJFX11. If I run the project within Eclipse, it loads the properties file. However, if I create a run-time image of my module with jlink, it can't find the properties file and the value of jarLocation is:
jarLocation = /cryostats
To the best of my knowledge, this happens because there is no concept of a class-path within a fully-modular Java application, so as a workaround, I have used
confLocation = System.getProperty("user.dir") + "/config/";
and I have manually copied the "config" folder to the "bin" folder after executing jlink. This works when I launch the application but it doesn't work from within Eclipse, and it's a crude and ugly solution.
Is there a way to keep the properties file accessible by the user within the run-time image generated by jlink and at the same time accessible by Eclipse?
It has never been correct to assume classes come from a .jar file which is a physical file in the platform’s file system. (Also, getCodeSource() can return null).
Windows, OS X, and Linux all have standard locations where user configuration files are stored. See Find place for dedicated application folder. To oversimplify the information from that link:
System.getProperty("user.home") + "\\AppData\\Roaming\\CryoStats"
System.getProperty("user.home") + "/Library/Application Support/CryoStats"
System.getProperty("user.home") + "/.config/CryoStats"
(Don’t just use those paths directly; it’s actually more complicated than this. Read the link for the full explanation.)
Never use System.getProperty("user.dir")
—that’s the current directory, and it could be anything, including the system’s root directory. On the other hand, the user.home
system property points to a stable location, namely the user’s home directory.
What I do for application configuration is: I include the default configuration inside the program, as a read-only resource. When the program exits, it saves that configuration, possibly modified by the user, to the system’s standard configuration location.