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

  1. Detect the current software version from the artifact metadata that is embedded into any Maven-built artifact
  2. Query Nexus for available artifacts for the installed software (identified by Maven groupId and artifactId).
  3. Test if any of the available artfacts has a version number higher than the installed one
  4. If a newer version is available then download it
  5. 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

Leave a Reply

green red blue grey