1

I've been reading tons of Stack Overflow posts about Java path encodings and proper ways to manage file paths (like this one). Still, I can't really get how to manage my file path.

What I want to achieve it's pretty straightforward: get my *.jar file path encoded in a proper way so that I may be able to use it as an input to FileInputStream(): in this way I can load a properties file which is located in the same folder of the aforementioned *.jar.

I've read Java documentation and I understood that URLDecoder just escapes special symbols (like "+") with blank spaces, so I can't really get which methods combination I've to use to get my absolute path to the file, even if its containing folders (not my fault) names' are made of white spaces and said symbols. This is my method:

private FileInputStream loadInputPropertiesFile() {
    FileInputStream input = null;

    // Let's load the properties file from the *.jar file parent folder.
    File jarPath = new File(PropertiesManagement.class.getProtectionDomain().getCodeSource().getLocation().getPath());
    String propertiesPath = String.format("%s/", jarPath.getParentFile().getAbsolutePath());

    propertiesPath = URLDecoder.decode(propertiesPath, StandardCharsets.UTF_8);

    // This part has been added just for tests, to better understand how file paths were encoded.
    try {
        URL url = jarPath.toURI().toURL();
        File path = Paths.get(url.toURI()).toFile();
        System.out.println(path);
    } catch (MalformedURLException | URISyntaxException e) {
        e.printStackTrace();
    }

    try {
        input = new FileInputStream(propertiesPath + CONFIG_FILE_PATH);
    } catch (FileNotFoundException e) {
        System.out.println("Properties file not found! Have you deleted it?");
        e.printStackTrace();
    }

    return input;
}

EDIT
The file path I'd like to get is something like this: "C:\Some + Folder\x64".
In the end, the returned input should be something like this: "C:\Some + Folder\x64\config.properties".

Davide3i
  • 1,035
  • 3
  • 15
  • 35

2 Answers2

1

You should just add the parent folder to the classpath, and load it as usual:

//load a properties file from class path, inside static method
Properties prop = new Properties();
try(final InputStream stream = 
   Classname.class.getClassLoader().getResourceAsStream("foo.properties")) {
    prop.load(stream);
}
// or load it within an instance
try(final InputStream stream =
    this.getClass().getResourceAsStream("foo.properties")) {
    prop.load(stream);
}

And instead of the "%s/", it would be better to use "%s" + File.separator, that will always gives you the platform-independent separator.

m4gic
  • 1,461
  • 12
  • 19
  • I think that in this way I'll look for the config.properties file inside of the jar, not in the same folder where they're both in. Thanks for the File.separator hint, though. – Davide3i May 31 '18 at 14:28
  • That's why I suggested to put the folder of the jars to the classpath. The classloader will check all entries in the classpath, if the classpath contains a folder, it will try to get it from there. – m4gic May 31 '18 at 14:35
  • I'm a bit lost, I'm sorry. I'm kind of new to Java, so I'll need to ask you about adding the parent folder to the classpath. I put the config.properties file outside of the jar file 'cause I also need to write on it. – Davide3i May 31 '18 at 14:38
  • No problem. You can execute your command like this: java -cp "c:\path\to\jar1.jar;c:\path\to\jar2.jar;c:\path\to" com.foo.bar.Main arg1 arg2... Of course your config.properties should be next to the jars in the c:\path\to folder. – m4gic May 31 '18 at 14:46
  • I'm trying to deploy this app without using any command line inputs, so a simple double click should be used to start it. Any way to do that just recycling my code, still using FileInputStream? – Davide3i May 31 '18 at 14:50
  • The "double click" works only if you don't need anything special like this. You have three options: either to create a cmd file with similar content I wrote, or you have to add that folder dynamically to the classpath as described it here https://stackoverflow.com/questions/7884393/can-a-directory-be-added-to-the-class-path-at-runtime but that is not a general solution and forces you to hardcode a path into the application. The third option is to define a JVM argument for this, and read it from the application, however that also requires a cmd file where that argument can be defined. – m4gic May 31 '18 at 15:01
  • +1: You can define an (system wide) environment variable and read the file location from there, in this case you can run it using your double click method but before you could do that that environment variable must be defined. In the cases of cmd files, you will run the application by double clicking on the cmd file, not on the jar file, this is the only difference. – m4gic May 31 '18 at 15:01
  • Ok! In any case, at the moment, my code is working. The only issue comes up with paths having symbols in them, 'cause URLDecoder change them to white spaces. Thank you for the insight you gave me! – Davide3i May 31 '18 at 17:55
0

In the end I fixed it and it's working for paths with special symbols, too.
Hope it will help someone having my same issue.

/**
 * It loads the 'config.properties' file into a FileInputStream object.
 *
 * @return The FileInputStream object.
 */
private FileInputStream loadInputPropertiesFile() {
    FileInputStream input = null;

    // Let's load the properties file from the *.jar file parent folder...
    File jarPath = new File(PropertiesManagement.class.getProtectionDomain().getCodeSource().getLocation().getPath());
    String propertiesPath = String.format("%s" + File.separator, jarPath.getParentFile().getAbsolutePath());
    propertiesPath = propertiesPath.replace("%20", "\u0020");

    try {   
        // ... and setting the pointer to the same path, ready to read the properties file.
        input = new FileInputStream(propertiesPath + CONFIG_FILE_PATH);
    } catch (FileNotFoundException e) {
        System.out.println("Properties file not found! Have you deleted it?");
        e.printStackTrace();
    }

    return input;
}
Davide3i
  • 1,035
  • 3
  • 15
  • 35