Dear developers,

After some months of reporting issues for Maven, I have noticed common bugs
in various plugins. For me it seems that many developers are either
completely unaware of the problems (i.e. do not handle them at all) or are
misunderstanding them (i.e. handle them insufficiently). Therefore, I would
like to illustrate the source of those problems in more detail such that
developers can start to prevent bugs right from the beginning rather than
fixing them afterwards.

1) Relative Paths

It is common practice for users of Maven to specify relative paths in the
POM, not to mention that the Super POM does so, too. The intention is almost
always to resolve such relative paths against the base directory of the
current project. In other words, the paths
 target/classes
and
 ${basedir}/target/classes
should resolve to the same directory for a given POM.

Unfortunately, the class java.io.File does not resolve relative paths
against the project's base directory. As mentioned in its class javadoc [0],
it resolves relative paths against the current working directory. In plain
English: Unless a Maven component has complete control over the current
working directory, any usage of java.io.File in combination with a relative
path is a bug.

At first glance, one might be tempted to argue that the project base
directory is equal to the current working directory. However, this
assumption is generally not true. Consider the following scenarios:

a) Reactor Builds
When a child module is build during a reactor build, the current working is
usually the base directory of the parent project, not the base directory of
the current module. That is the most common scenario where users are faced
with the bug.

b) Embedded Maven Invocations
Other tools, most prominently IDEs, that run Maven under the hood may have
set the current working directory to their installation folder or whatever
they like.

c) Maven Invocations using the -f switch
While it is surely an uncommon use-case, the user is free to invoke Maven
from an arbitrary working directory by specifying an absolute path like
 mvn -f /home/me/projects/demo/pom.xml

In order to guarantee reliable builds, Maven and its plugins must manually
resolve relative paths against the project's base directory. A simple idiom
like the following should do just fine:

 File file = new File( path );
 if ( !file.isAbsolute() )
 {
   file = new File( project.getBasedir(), file );
 }

Many Maven plugins can get this resolution automatically if they declare
their affected mojo parameters of type java.io.File instead of
java.lang.String. This subtle difference in parameter types will trigger a
feature that seems to be known as "path translation", i.e. Maven itself will
properly resolve relative paths when it pumps the XML configuration into a
mojo.

While it will not cure this problem completely, I suggest to review the
MavenProject API such that it ensures the following invariant: All paths
reported by means of a getter-like method are absolute. This basically
requires to resolve all relative paths that are pushed in by a setter-like
method. MavenProject.addCompileSourceRoot() is just one example for a public
API method that currently blindly accepts a relative path, giving rise to
later failures because a misbehaving component resolves this path not
properly.

2) Resource Bundle Families

Especially reporting plugins employ resource bundles to support
internationalization. One language (usually English) is provided as the
fallback/default language in the base resource bundle. Due to the lookup
strategy performed by ResourceBundle.getBundle() [1], one must always
provide a dedicated resource bundle for this default language, too. This
bundle can be empty because it inherits the strings via the parent chain
from the base bundle, but it must exist.

Take the following example. A report mojo uses a resource bundle family with
the base name "mymojo-report" and erroneously provides only the following
bundles:
- mymojo-report.properties      (base bundle for English)
- mymojo-report_de.properties   (specific bundle for German)
Further assume that the default locale of the current JVM is "de". Now, when
ResourceBundle.getBundle() is called to retrieve the bundle for locale "en",
it will try the following candidate bundles and use the first one found:
- mymojo-report_en.properties   (candidate for requested locale)
- mymojo-report_de.properties   (candidate for default locale)
- mymojo-report.properties      (base bundle, always last)
Because "mymojo-report_en.properties" does not exist, the bundle
"mymojo-report_de.properties" will be chosen instead of the base bundle
which would have provided the requested English translation.

Plugin developers can easily expose this bug when calling
 mvn site -Dlocales=xy,en
where "xy" denotes some other language supported by the plugin. Specifying
"xy" as the first locale will have the Maven Site Plugin change the JVM's
default locale to "xy" which in turn causes the lookup for "en" to fail as
outlined above.

Thanks for your attention,


Benjamin Bentmann


[0] http://java.sun.com/javase/6/docs/api/java/io/File.html
[1]
http://java.sun.com/javase/6/docs/api/java/util/ResourceBundle.html#getBundle(java.lang.String,%20java.util.Locale,%20java.lang.ClassLoader)
[2] http://jira.codehaus.org/browse/MNG-3273


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to