Maven 2: Inheritance before Interpolation
Some days ago I came along a problem with our beloved build tool Maven2. Since this was the first real problem with the tool I could not solve or find a good workaround which I think is worth a blog post.
Maven 2 relies on a project descriptor for each project to build, which is XML in a file called pom.xml at the root of a project. Within that file you define how your project is to be built, what dependencies it needs and much more.
It is a commonly used pattern to avoid a monolithic build and divide your stuff to several smaller projects as the project grows (e.g. having an api-package, a core, an web-client and so on). Since all of these projects actually belong together you may want to avoid duplication of xml within the project descriptors. So maven provides a powerful feature for this: pom inheritance. Each project may have a project as parent from whose pom information gets inherited. Using this you can define common stuff like repositories, reporting-settings and project information at the the “parent-pom”.
So one of the entries we defined in the parent-pom is the repository-node which tells Maven places where it looks for packages needed for the build (e.g. dependencies). In our case this points to a repository hosted by ourselves at an instance of Nexus.
<repository>
<id>internal.nexus.example.org</id>
<name>Internal Repo</name>
<url>${infrastructure.nexus.host}/content/groups/internal</url>
</repository>
As you can see we defined the hostname of the repository as a property which Maven resolves during the evaluation of the project descriptor. This process is called Interpolation. At some other place (within a pom or within global configuration) you are able to set the property infrastructure.nexus.host
. We did this since the hostnames are subject to change, as soon as the projects infrastructure is moved to our customers.
Ok, the first (solvabe) problem that occurred here is some kind of a chicken-egg problem. The parent itself is hosted at the internal repository which makes it impossible for maven to download before it knows from which repository.
So maven has to resolve inheritance first which means the repository, where the parent of a project can be downloaded must be either defined in the child’s pom or the parent must be available in the global maven central repository (which was not an option for us). I know there is another way which means defining the repository in the global settings.xml file which I wanted to avoid because it brings along other, in my opinion even bigger, problems.
So we decided to add the repository-node (as seen above) to each projects’ pom.xml so that maven can download the parent project from the internal Nexus first. Since we still needed to be able to override the hostname the property still needed to be defined somewhere else than in the parent (e.g. on commandline or using settings.xml).
But as soon as you try to build your project maven fails:
$ mvn validate -Dinfrastructure.nexus.host=http://example.org
[INFO] Scanning for projects...
Downloading: ${infrastructure.nexus.host}/content/groups/internal/org/example/parent/1.0.0-SNAPSHOT/parent-1.0.0-SNAPSHOT.pom
[INFO] Unable to find resource 'org.example:parent:pom:1.0.0-SNAPSHOT' in repository internal.nexus.example.org (${infrastructure.nexus.host}/content/groups/internal)
[INFO] ------------------------------------------------------------------------
[ERROR] FATAL ERROR
...
As you could see, in the URL where maven tried to download the parent-project it did not resolve the property.
This is because maven FIRST does inheritance and THEN interpolation. Its clear that it cannot do it the other way round (since the parent project may define properties that are needed for interpolation). But I cannot understand why maven does not do FIRST interpolation, THEN inheritance and THEN interpolates again. Does anyone know?
To “solve” our problem we currently do not use interpolation within the child’s repository-configuration which sucks because it means to change a lot of pom.xml files later (and even released ones…).