Maven Virtual Versions: Let's Fix this Mess!


March 12, 2009 By oleg

mavenMaven introduced a very useful idea – “virtual” versions: SNAPSHOT, LATEST, RELEASE. While this is an interesting and powerful feature, I’ve found that people still don’t have a firm grasp of how virtual version work and of some of the problems with SNAPSHOT versions. Depending on how you use and/or understand it, this feature can cut both ways. In this post, I take a closer look at Maven’s Virtual Versions and try to provide some clarity and definition.

In theory, any dependency can specify a virtual version:

- Modifier __SNAPSHOT__ is a modifier for a “base” version. If you specify _1.0-SNAPSHOT_, this translates to “give me the latest development version for the base 1.0″. The assumption behind the SNAPSHOT modifier is that developers are continuously pumping out version after version in order to get to *1.0*. A development team is constantly and repeatedly deploying snapshot into repositories, and others can use them without requesting the exact version.
- Version name __LATEST__. This is used without a *base* version and simply means – find and give me the latest possible version of an artifact be it a SNAPSHOT or a RELEASE.
- Version name __RELEASE__ . Same as __LATEST__, but excludes any __SNAPSHOT__ dependencies.

The last two are true virtual versions as there is no POM with such version set. In Maven 2, virtual versions are only used by plugin dependencies, __Mercury__ treats them as first class citizens and can process virtual versions anywhere in artifact metadata (with the exception of version range definitions).

SNAPSHOT is a much more convoluted virtual version, and there are a lot of problems and contradictions involved in the resolution of SNAPSHOT dependencies:

The local repository could house both timestamped snapshot binaries and -SNAPSHOT binaries, like mercury-artifact-1.0-alpha-6-20090305.234653-4.jar and mercury-artifact-1.0-alpha-6-SNAPSHOT.jar. __Let’s call them timestamped snapshots (TS) and non-timestamped snapshots (SN) respectively__ . While the install plugin does create non-timestamped (SN) binaries, the deploy plugin only creates timestamped snapshots (TS) binaries in the remote repopository. Remote repositories usually contain only timestamped (TS) snapshots, but I saw several cases where people copied the contents of a local repository to a server and hoped to use it as a remote repository.

The file’s last modification date on timestamped snapshot (TS ) could be before or after the non-timestamped snapshot (SN), if one exists. And it’s hard to select a correct version based on either name or modification date.

All these different options prevent consistent processing of virtual versions by repository management code. While LATEST and RELEASE are more or less well defined and allow formal processing, SNAPSHOT leaves a lot to wish for. The above listed scenario’s don’t allow a consistent treatment of snapshots. Take for example Mercury – if it uses a _select non-timestamped (SN) artifacts if they exist_ strategy, there is a good chance that the non-timestamped artifact isn’t as recent as the timestamped artifact – leading to possible compilation errors. On the other hand, if Mercury tries to avoid this situation by looking at file timestamps, one bad file date could wreak havoc.

To summarize – there is no “right” solution, as each approach could be broken easily by primitive and often inevitable repository modifications. By removing these literal “SNAPSHOT” files and requiring all snapshots to be timestamped, we can make the SNAPSHOT virtual version as consistent and reliable as the RELEASE and LATEST virtual version. Mercury, tries to be as backward-compatible as possible, recreating the already established algorithm for resolving SNAPSHOT dependencies.

The real solution, the way to fix this mess, is to get rid of literal “SNAPSHOT” files in the repository altogether.