Using Nexus and the Nexus REST API for implementing a software update tool
Nexus is using a pretty well documented REST API which is usable externally as well.
For one of my customers I implemented a kind of automatic software update tool that can be embedded into any product. It is based on the Sonatype NEXUS repository manager.
That tool performs the following steps
- Detect the current software version from the artifact metadata that is embedded into any Maven-built artifact
- Query Nexus for available artifacts for the installed software (identified by Maven groupId and artifactId).
- Test if any of the available artfacts has a version number higher than the installed one
- If a newer version is available then download it
- Move the new version to the target directory (e.g. the deployment folder of the application server)
The Nexus REST url for a simple query for some artifact looks like http://localhost/nexus/service/local/data_index/repositories/releases/content?q=commons-lang. This query asks for the commons-lang artifact from the releases repository.
More URL schemes are documented here and there.
The result from this query is like (only the latest versions are included for brevity):
<?xml version="1.0" encoding="UTF-8"?> <search-results> <totalCount>5</totalCount> <from>-1</from> <count>-1</count> <tooManyResults>false</tooManyResults> <data> <artifact> <resourceURI>http://localhost/nexus/service/local/repositories/central/content/commons-lang/commons-lang/2.4/commons-lang-2.4.jar</resourceURI> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.4</version> <packaging>jar</packaging> <extension>jar</extension> <repoId>central</repoId> <contextId>Maven Central (Cache)</contextId> <pomLink>http://localhost/nexus/service/local/artifact/maven/redirect?r=central&g=commons-lang&a=commons-lang&v=2.4&e=pom</pomLink> <artifactLink>http://localhost/nexus/service/local/artifact/maven/redirect?r=central&g=commons-lang&a=commons-lang&v=2.4&e=jar</artifactLink> </artifact> <artifact> <resourceURI>http://localhost/nexus/service/local/repositories/central/content/commons-lang/commons-lang/2.4/commons-lang-2.4-sources.jar</resourceURI> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.4</version> <classifier>sources</classifier> <packaging>jar</packaging> <extension>jar</extension> <repoId>central</repoId> <contextId>Maven Central (Cache)</contextId> <pomLink></pomLink> <artifactLink>http://localhost/nexus/service/local/artifact/maven/redirect?r=central&g=commons-lang&a=commons-lang&v=2.4&e=jar&c=sources</artifactLink> </artifact> </data> </search-results>
That only needs to be parsed using a SAX parser for extracting the most important elements. This is the Maven GAV, packaging, classifier, and the download url which are stored in a POJO. This POJO implements the algorithm for selecting the latest version by its compareTo method.
public class MavenArtifactMetadata implements Comparable<MavenArtifactMetadata> {
private String groupId;
private String artifactId;
private String version;
private String resourceURI;
private String packaging;
private String classifier;
@Override
public int compareTo(MavenArtifactMetadata o) {
if (!this.groupId.equals(o.groupId) || !this.artifactId.equals(o.artifactId)) {
return -1;
}
return versionCompareTo(o.version);
}
int versionCompareTo(String version) {
Integer[] thatVersion = versionComponents(version);
Integer[] thisVersion = versionComponents(this.version);
int length = Math.min(thatVersion.length, thisVersion.length);
for (int i = 0; i < length; i++) {
if (thatVersion[i].equals(thisVersion[i])) {
continue;
}
return thisVersion[i].compareTo(thatVersion[i]);
}
return thisVersion.length - thatVersion.length;
}
Integer[] versionComponents(String version) {
String[] splitted = StringUtils.delimitedListToStringArray(version, ".");
if (splitted.length>0 && splitted[splitted.length-1].endsWith("-SNAPSHOT")) {
splitted[splitted.length-1]=splitted[splitted.length-1].replaceAll("-SNAPSHOT", "");
}
Integer[] components = new Integer[splitted.length];
for (int i = 0; i < splitted.length; i++) {
components[i] = (Integer.valueOf(splitted[i]));
}
return components;
}
}
While implementing the download part I had learned an important lesson. For downloading and moving large files it is much more efficient to use the Java NIO API.

2 Comments
Gabriel Falkenberg - 2011/02/22
Nice idea! Nexus contains APIs that will download the latest version with no XML-parsing. The documentation is inside of Nexus at (I use Nexus 1.9 M1):
http://example.com/nexus/nexus-core-documentation-plugin/core/docs/rest.artifact.maven.redirect.html
If Nexus was exposed at example.com/nexus
Maybe they support conditional GETs on that URL?
Martin Ahrer - 2011/02/23
Thanks for that hint. The Nexus I was working with at that time was fairly old. So either it was not supported back then or I didn’t realize that this feature was built in. Also this URL may have been hidden because of some incomplete authorization setup which can be pretty nasty!
For everyones reference I’m adding your URL pointing to the public sonatype repository docs:
https://repository.sonatype.org/nexus-core-documentation-plugin/core/docs/rest.artifact.maven.redirect.html