If your application is bundled as a jar file, then MainApp.class.getProtectionDomain().getCodeSource().getLocation().toURI()
will return a jar:
scheme URI. The constructor for File
taking a URI
assumes it gets a file:
scheme URI, which is why you are getting an error here. (Basically, if your application is bundled as a jar file, the resource config.properties
is not a file at all, its an entry in an archive file.) There's basically no (reliable) way to update the contents of the jar file bundling the application.
The way I usually approach this is to bundle the default configuration file into the jar file, and to define a path on the user file system that is used to store the editable config file. Usually this will be relative to the user's home directory:
Path configLocation = Paths.get(System.getProperty("user.home"), ".applicationName", "config.properties");
or something similar.
Then at startup you can do:
if (! Files.exists(configLocation)) {
// create directory if needed
if (! Files.exists(configLocation.getParent())) {
Files.createDirectory(configLocation.getParent());
}
// extract default config from jar and copy to config location:
try (
BufferedReader in = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/config.properties")));
BufferedWriter out = Files.newBufferedWriter(configLocation);) {
in.lines().forEach(line -> {
out.append(line);
out.newLine();
});
} catch (IOException exc) {
// handle exception, e.g. log and warn user config could not be created
}
}
Properties config = new Properties();
try (BufferedReader in = Files.newBufferedReader(configLocation)) {
config.load(in);
} catch (IOException exc) {
// handle exception...
}
So this checks to see if the config file already exists. If not, it extracts the default config from the jar file and copies its content to the defined location. Then it loads the config from the defined location. Thus the first time the user runs the application, it uses the default configuration. After that, the user can edit the config file and subsequently it will use the edited version. You can of course create a UI to modify the contents if you like. One bonus of this is that if the user does something to make the config unreadable, they can simply delete it and the default will be used again.
Obviously this can be bullet-proofed against exceptions a little better (e.g. handle case where the directory is unwritable for some reason, make the config file location user-definable, etc) but that's the basic structure I use in these scenarios.