My Jude 1.00 release shipped with the following method in it to determine its own installation directory:
static File getInstallationDirectory() {
java.net.URL url = Main.class.getResource("Main.class");
String protocol = url.getProtocol();
File f = null;
if (protocol.equals("file")) {
// Go up twice for jude/Main.class
f = new File(url.getPath()).getParentFile().getParentFile();
}
else if (protocol.equals("jar")) {
String s = url.toString();
int pos = s.lastIndexOf('!'); // ! marks beginning of jar entry
s = s.substring(9, pos); // 9 is the length of "jar:file:"
f = new File(s).getParentFile();
}
return f;
}
It uses Class.getResource() to get a URL for one of its own class files. Then it parses this URL (which may be a file: or jar: URL) to determine the installation directory. I think its a clever thing to do. But ithere is a bug (which has been fixed in Jude 1.01, which I've just releases). Can you spot it? (The answer is below)
I got a bug report this morning from someone who had tried to install Jude in c:\Program Files on a Windows system. Jude wouldn't start up from this directory because it couln't find its license file. It was looking for a directory named "Program%20Files". Spaces aren't legal characters in URLs, so the java.net.URL class uses the correct %20 escape sequence. Unfortunately, the URL class isn't smart enough to unescape those characters.
Fortunately the java.net.URI class (added in Java 1.4) is smart enough. This code fixes the bug:
static File getInstallationDirectory() {
java.net.URL url = Main.class.getResource("Main.class");
java.net.URI uri = java.net.URI.create(url.toString());
String scheme = uri.getScheme();
File f = null;
if (scheme.equals("file")) {
// Go up twice for jude/Main.class
f = new File(uri.getPath()).getParentFile().getParentFile();
}
else if (scheme.equals("jar")) {
String s = uri.getSchemeSpecificPart();
int pos = s.lastIndexOf('!'); // ! marks beginning of jar entry
s = s.substring(5, pos); // 5 is the length of "file:"
f = new File(s).getParentFile();
}
return f;
}
The moral of this story, I suppose, is to use the URL class when you actually want to retreive the resource referred to by the URL. But if you just want to parse or manipulate the URL, then use the URI class.
Feel free to adapt the getInstallationDirectory() method above for your own use if you like it (Note that it is specific to Jude in a couple of places). And if you've got another way to find the install dir for an application, please share in the comments.




yeah. it's nasty. if you need to go t'other way about, do this:
File f = new File(...);
URL u = f.toURI().toURL();
There is a better way to figure out the locations, independent of particular class name and package:
CLASS.getProtectionDomain().getCodeSource().getLocation().toURI().normalize()
Because of this problem toURL() is deprecated in Java 6.