Relative File Path Anomalies
Posted by Trejkaz Fri, 28 Jan 2005 02:29:00 GMT
Sometimes the subtlety in Java is almost enough to be annoying.
For instance, let’s take the java.io.File constructors for today. Ignoring the versions of the constructors which take File objects themselves, you basically have a choice between three ways to construct your File path.
- Use an absolute path (almost never used if you want portability, since an absolute path inevitably requires knowing what OS you’re using before you create it);
- Use a relative path, providing the base path to resolve it relative to.
- Use a relative path, providing no base path.
The last option is where problems will occur. Suppose you create a file:
File file = new File("foo.txt");
As you can probably guess, this is supposed to point at the file called “foo.txt” relative to the current directory. But what you might not expect, is that this is not always the case.
Suppose you have a native library loaded at some point in your application. This native library then does the unthinkable, and changes the current working directory. As soon as this happens, everything goes to shit.
System.out.println(System.getProperty("user.dir"));
File file = new File("foo.txt");
System.out.println(file);
System.out.println(file.getAbsoluteFile());
InputStream stream = new FileInputStream(file);
stream.close();
If you run this after some cretinous native library has changed the path, you’ll see something like this:
/home/trejkaz/test
foo.txt
/home/trejkaz/test/foo.txt
java.io.FileNotFoundException: Cannot find the file: foo.txt
As far as the first three lines go, everything looks okay. And looking in the current directory, you’ll see that “foo.txt” is indeed present. Yet, the file doesn’t exist… why?
Well, it turns out that non-absolute file paths are resolved relative to the new current directory. And what’s funny, and perhaps wrong on the part of the Java API, is that calling getAbsoluteFile() on that File object actually results in Java resolving it relative to the value of the “user.dir” property, instead of the real current directory. So it will appear that the file path is right, even though it’s wrong.
So this is why you should always call getAbsoluteFile() on any paths which are created which weren’t already absolute, and/or construct them relative to some other explicit path.
And I suppose it’s also why native libraries are bad, and why everything should just be implemented in Java in the first place. :-)