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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?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&amp;g=commons-lang&amp;a=commons-lang&amp;v=2.4&amp;e=pom</pomLink>
          <artifactLink>http://localhost/nexus/service/local/artifact/maven/redirect?r=central&amp;g=commons-lang&amp;a=commons-lang&amp;v=2.4&amp;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&amp;g=commons-lang&amp;a=commons-lang&amp;v=2.4&amp;e=jar&amp;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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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.

Comments