[RFE] Control Relative Path Resolution

Robert Scholte rfscholte at apache.org
Tue Feb 12 07:26:14 UTC 2019

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


# 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:

  - pom.xml
  \- M1
  |   - pom.xml
  \- M2
      - pom.xml|
      \- M3

(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