Context: My software depends on calling a library which can only accept relative paths as an input because of an old limitation. I need the paths to be relative to a known directory. The library might make a call internally like
java.io.File fooBar = new java.io.File("foo/bar");
I need this to give me /nwd/foo/bar
and not, say, /cwd/foo/bar
where /cwd
is the working directory from which java
was run.
For all intents and purposes, I cannot modify the internal behavior of this library. Manually overriding the methods which instantiate these objects would involve basically rewriting the entire library.
A tempting solution would be to just System.setProperty("user.dir", "/nwd")
before calling the library, but this doesn't actually give me the desired effect. Indeed, if I called fooBar.getAbsolutePath()
, I would get the desired /nwd/foo/bar
, but if I checked fooBar.exists()
or tried to open the file for reading or writing, it would appear that the file doesn't exist, because it's actually trying to open /cwd/foo/bar
. In fact, if fooBar
were instead initialized by
java.io.File fooBar = new java.io.File(new java.io.File("foo/bar").getAbsolutePath());
that would actually work, because then the File
object actually contains absolute references.
At this point, I'm so frustrated that I don't care if this requires a hacky solution. I just need the effect of changing the working directory.
A big big big disclaimer here! Don't do this!!!!
You aren't supposed to be able to set the root directory once it is set. I'm not exactly sure what the design philosophy is here, but I imagine it's to do with security and not allowing malicious code to get your application to do something unexpected. That said, there are ways we can get around this using some nasty reflection.
I achieved this hack by debugging where the
Paths
class finds it's default directory and loads files from. I use reflection to edit the values of theFileSystem
class which I saw that it uses. This hack is going to be operating system dependent as it edits theFileSystem
instance. It might also need to be tweaked as there's no guarantee that the instance wont be renewed. You'll have to make sure to test it on all the target systems as they will have differentFileSystem
implementations (BUT PLEASE DON'T ACTUALLY DO THIS).Further with the new changes in Java 9 you wont be able to make this hack work easily (they disallow using reflection to edit classes not exposed by the "module"). Your best bet is to spin up a new JVM instance and make the call there with the
-Droot
property passed in. It's not a pretty solution but neither is the code-base you're working with. You should actively attempt to fix this in a more sensible manner. (Maybe a re-write of the library is the best way to go if the business function is actually important)Working demo with a file in my working directory and a file in my
C:\\
:This outputs:
Please note and heed the warnings the JVM just produced.
A very alternative way to change the CWD of the library would be to launch it in a different Java process, for which you can specify CWD at launch time (see for example ProcessBuilder documentation). If I understand the problem correctly, your current flow is somewhat similar to
The new flow would be
Of course, this will force you to write a full wrapper, and communicate it using sockets&serialization, or any other communication strategy of your choice. On the plus side, this will allow you to launch several instances of your library side-by-side, without their CWDs interfering with each other -- at the cost of JVM and communication overheads.
why not copy the file from /nwd/foo/bar to /cwd/foo/bar then you can use your existing library as it is :)
I don't think this is a big issue, because a relative path can start from root as well.
So let assume your current directory is
/user/home
and you want to refer to/user/tarun/foo/bar
then you would give the relative path to be../tarun/foo/bar
to your library.And for that you can use the code as discussed in below SO thread
How to construct a relative path in Java from two absolute paths (or URLs)?