[RFE] Control Relative Path Resolution

Robert Scholte rfscholte at apache.org
Thu Mar 7 18:52:35 UTC 2019

In conclusion: System properties is not an option.
However, the original request stays: being able to control relative path  

In search for another solution I had a look at an approach mentioned by  
Alan: the FileSystem.
It was possible to make it work for java.nio.file.Path, but not for  
But even for Path I had to wrap several classes, which is not my preferred  
solution, since the wrapped instance will never be able to call a  
reimplemented method like toAbsolutePath().
Controlling the root of a FileSystem seems the missing feature here.

I'm open for any proper solution, but it seems this can be fixed within  
the JVM, ideally for both File and Path to keep all those Java projects  
working with paths relative to the pom's directory.


[1] https://github.com/apache/maven-studies/tree/maven-basedir-filesystem

On Tue, 12 Feb 2019 08:26:14 +0100, Robert Scholte <rfscholte at apache.org>  

> Recently an interesting change has been applied to the codebase of the  
> JDK: Several predefined System properties are now read-only. This makes  
> the runtime more robust, so we consider this as a great improvement.
> However, this introduces a new challenge, in this case regarding the  
> system property 'user.dir', since it has different purposes. Some Maven  
> plugins change this value in some cases as it is the only way to control  
> the resolution of relative paths. Being able to change the "working  
> directory" gives several interesting benefits, which should not impact  
> other usages of user.dir.
> TLDR: As a Java User
>        I want a *standard* way to control the resolution of a File with  
> a relative path
>        So that I'm not forced to start a new JVM
> Backgrounds:
> # How java.io.File works (from JavaDoc):
> ...
> A pathname, whether abstract or in string form, may be either absolute  
> or relative. An absolute pathname is complete in that no other  
> information is required in order to locate the file that it denotes. A  
> relative pathname, in contrast, must be interpreted in terms of  
> information taken from some other pathname. By default the classes in  
> the java.io package always resolve relative pathnames against the  
> current user directory. This directory is named by the system property  
> user.dir, and is typically the directory in which the Java virtual  
> machine was invoked.
> ...
> # How Apache Maven works:
> root
>   - pom.xml
>   \- M1
>   |   - pom.xml
>   \- M2
>       - pom.xml|
>       \- M3
>           -pom.xml
> (when talking about a Maven module, this is a directory with a pom.xml  
> containing instructions, in general to compile sources of one of the  
> subfolders and to package it into a jar)
> Maven has the concept of a multi-module project, which is a tree of  
> directories containing several pom.xml files. There are in this picture  
> 2 kinds om poms: aggregator-poms to trigger other poms and project-poms  
> to compile,test and package the content of that folder. It is possible  
> to start anywhere in this tree. In fact it is even possible to start  
> outside this tree and run maven like 'mvn -f /path/to/any/pom.xml'. This  
> should clearly explain there's no relation between the starting  
> directory (user.dir) and the working directory.
> In Maven the best practice is to use relative paths when working with  
> files. This is always relative to the folder containing the pom.xml of  
> that Maven module. This way we can ensure that files are always  
> calculated correctly no matter where and how you start Maven. For  
> instance, IDE's follow the same concept.
> # In practice
> What we also see is that people are having problems understanding  
> inputstreams. The following codefragments we see quite often:
> File f = new File( "src/test/resources/README.md" );
> FileReader is = new FileReader( f );
> Knowing that files under src/test/resources end up on the classpath,  
> this is the preferred solution:
> InputStream is = MyClass.class.getResourceAsStream("/README.md");
> However, others feel more comfortable with URLs and Files, resulting in:
> URL url = MyClass.class.getResource("/README.md");
> File f = url.getFile();
> FileReader is = new FileReader( f );
> (this works as long as the compiled classfiles are in a directory and  
> not in a jar)
> Depending on the knowledge, unexperienced developers tend to fall back  
> to Files when possible. However, they do understand the difference  
> between relative and absolute paths.
> # Evaluation
> This topic has been discussed with several OpenJDK developers from  
> Oracle and committers of the Apache Maven team at FOSDEM 2019 and we  
> came to the following conclusions:
> Consider: File f = new File( "a/b/c" );
> Available solutions:
> * Use the classpath as much as possible
>    - not always possible, in some cases you are forces to work with  
> files.
> * Start a new JVM from the expected directory
>    Consequences are:
>    - longer execution time (even though the JVM startup time has  
> improved over the years)
>    - more memory consumption
>    (we recently received an email from somebody having a Maven  
> Multimodule project of ~1600 modules, getting close to its limits. For a  
> project like this, being forced to fork has a negative impact)
> * File f = new File( System.property( "file.basedir", ""), "a/b/c" );
>    Consequences are:
>    - Developers must rewrite their code, including every time the *same*  
> statement when relative paths are used
>    - The execution framework should provide this 'magic' property and  
> might pick their own name. E.g. in cases of unittests it would be best  
> to have the same name used by Surefire(Maven) and all IDEs, since they  
> all have their own test execution framework.
> # Proposal
> All the options above would not require any changes to the JDK. However,  
> after rethinking these solutions with the Maven committers we would like  
> to see a small change in the JDK itself.
> The Java community would benefit if the JDK would specify 1 *mutable*  
> System property to control the basedir of a File. This property would  
> default to the value of 'user.dir' to keep the current behavior. With  
> this there is exactly 1 way for all to adjust the resolution of relative  
> paths.
> This would make it possible to remain the original behavior (users don't  
> have to change their code) and to make the JDK more robust by keeping  
> user.dir read-only.
> With regards,
> Robert Scholte

More information about the jdk-dev mailing list