[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
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