Do not call the URL.getPath() method. It does not return a valid filename, as you are now seeing. It just returns the path portion of the URL—the part after the scheme and host—with all percent escapes intact.
The correct way to convert a URL to a File is by converting it to a URI, then constructing a File from that URI.
So, the proper code would look like this:
CodeSource source = Main.class.getProtectionDomain().getCodeSource();
if (source != null) {
try {
File jarFile = new File(source.getLocation().toURI());
execPath = String.valueOf(jarFile.getParentFile());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
Notice the check for null. The code source can be null, depending on how much information a ClassLoader is willing and able to impart to the ProtectionDomain it creates. This means that trying to find the location of a .jar file at runtime is not reliable.
If you want to bundle native executables with a .jar file, here are some more reliable options:
- Include a script (batch file for Windows, shell script for other systems) with your .jar file that passes a system property to the program, which contains the location of the executables.
- Bundle the executables inside the .jar file, and at runtime, copy them to temporary files and make them executable.
The first approach would use the system to determine the directory, since that is reliable, and would pass it to the program. For instance, in Windows:
set execdir=%~dp0..\..
javaw.exe "-Dexecutable.dir=%here%" -jar MyApplication.jar
In Linux:
execdir=`dirname $0`/..
java "-Dexecutable.dir=$execdir" -jar MyApplication.jar
The second approach would require creating your .jar file with the executables inside it, then copying them outside the .jar when you want to run them:
Path execDir = Files.createTempDirectory(null);
execPath = execDir.toString();
String[] executables;
String os = System.getProperty("os.name");
if (os.contains("Windows")) {
executables = new String[] {
"windows/foo.exe", "windows/bar.exe", "windows/baz.exe"
};
} else if (os.contains("Mac")) {
executables = new String[] {
"mac/foo", "mac/bar", "mac/baz"
};
} else {
executables = new String[] {
"linux/foo", "linux/bar", "linux/baz"
};
}
for (String executable : executables) {
String baseName = executable.substring(executable.lastIndexOf('/') + 1);
Path newFile = execDir.resolve(baseName);
try (InputStream executable =
Main.class.getResourceAsStream(executable)) {
Files.copy(executable, newFile);
}
if (Files.getFileStore(newFile).supportsFileAttributeView(
PosixFileAttributeView.class)) {
Set<PosixFilePermission> perms =
Files.getPosixFilePermissions(newFile);
perms.add(PosixFilePermission.OWNER_EXECUTE);
Files.setPosixFilePermissions(newFile, perms);
}
}