In diesem Artikel wird gezeigt, wie die aktuelle Maven SNAPSHOT-Version für ein Maven-Artifact automatisch aus den Maven-XML-Metadaten ausgelesen und daraus dynamisch ein Download-Link generiert werden kann. Dies ist besonders nützlich für Dokumentationsseiten, die immer auf die aktuellsten Entwicklerversionen verweisen sollen.
Die Lösung ist auch deshalb relevant, weil das Sonatype-Snapshot-Repository derzeit keine Möglichkeit bietet, die aktuellsten SNAPSHOT-Versionen über eine Weboberfläche anzuzeigen.
Auch Änderungen an Minor-Versionen wie 2025.12.0-SNAPSHOT, 2025.12.1-SNAPSHOT etc. werden dabei berücksichtigt.
Gesucht wurde eine Lösung, die ohne externe JavaScript-Abhängigkeiten in jedem modernen Browser lauffähig ist.
Zunächst werden Links mit speziellen Data-Attributen für das Maven-Artifact eingefügt:
<a class="maven-snapshot-link"
data-maven-groupid="org.mycore"
data-maven-artifactid="mycore-base"
data-maven-version="2025.12"
data-maven-extension="jar">
mycore-base $VERSION ($SNAPSHOT)
</a>
Der Versions-String wird rechtstrunkiert verarbeitet, sodass auch Minor-Releases automatisch berücksichtigt werden. Mit dem Wert 20 würde man auch jahresübergreifend die aktuellste Version ermitteln. Die Platzhalter $VERSION und $SNAPSHOT werden später durch die tatsächlichen Versionsnummern ersetzt.
Beim Laden der Seite werden alle Links mit dem entsprechenden class-Attribut verarbeitet:
document.addEventListener("DOMContentLoaded", async function(event) {
for (let e of document.querySelectorAll("a.maven-snapshot-link")) {
const mavenData = await retrieveMavenData(e);
if(mavenData != null) {
e.setAttribute("href", mavenData.url);
e.setAttribute("data-maven-metadata-url", mavenData.metadataURL);
e.textContent = e.textContent
.replace("$VERSION", mavenData.version)
.replace("$SNAPSHOT", mavenData.snapshotVersion);
}
}
});
retrieveMavenData(e)Die Funktion liefert ein JSON-Objekt mit den relevanten Informationen und Links zu einem Maven-Artifact zurück. Als Parameter wird das HTML-Link-Element übergeben, in dessen data-*-Attributen die notwendigen Parameter enthalten sind. Im ersten Schritt wird mit der Funktion retrieveMavenMetadataURL(e) die maven-metadata.xml für die aktuellste verfügbare SNAPSHOT-Version ermittelt.
async function retrieveMavenData(e) {
const metadataURL = await retrieveMavenMetadataURL(e);
if(metadataURL != null) {
// Metadaten-XML über CORS-Proxy laden
const text = await fetch("https://corsproxy.io/?url=" + metadataURL)
.then((response) => response.text());
// XML parsen
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, "text/xml");
// Versionsinformationen extrahieren
const ext = e.dataset.mavenExtension;
const snapshotVersion = xmlDoc.evaluate(
"//snapshotVersion[extension='" + ext + "']/value",
xmlDoc, null, XPathResult.STRING_TYPE, null
).stringValue;
const version = xmlDoc.evaluate(
"/metadata/version",
xmlDoc, null, XPathResult.STRING_TYPE, null
).stringValue;
// Download-URL konstruieren
const downloadURL = "https://central.sonatype.com/repository/maven-snapshots/"
+ e.dataset.mavenGroupid.replaceAll(".", "/")
+ "/" + e.dataset.mavenArtifactid + "/" + version
+ "/" + e.dataset.mavenArtifactid + "-" + snapshotVersion
+ "." + e.dataset.mavenExtension;
return {
version: version,
snapshotVersion: snapshotVersion,
url: downloadURL,
metadataURL: metadataURL
};
}
return null;
}
Access-Control-Allow-Origin: *, damit die externe Datei auf der eigenen Webseite weiterverarbeitet werden kann.data-*-Attributen auslesen. Zu beachten ist, dass bei den Attribut-Namen eine Konvertierung von dash-style nach camelCase erfolgt.evaluate()-Funktion das Ergebnis (hier: ein String) weiterverarbeiten zu können.retrieveMavenMetadataURL(e)Diese Funktion ermittelt für das in den data-*-Attributen des übergebenen HTML-Elements spezifizierte Maven-Artifact den Link zur maven-metadata.xml für die aktuellste verfügbare SNAPSHOT-Version.
async function retrieveMavenMetadataURL(e) {
// Basis-Metadaten-URL konstruieren
let versionXmlURL = "https://central.sonatype.com/repository/maven-snapshots/"
+ e.dataset.mavenGroupid.replaceAll(".", "/")
+ "/" + e.dataset.mavenArtifactid
+ "/maven-metadata.xml";
// XML laden
const text = await fetch("https://corsproxy.io/?url=" + versionXmlURL)
.then((response) => response.text());
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, "text/xml");
const version = e.dataset.mavenVersion;
// Neueste Version mit dem angegebenen Präfix finden
const snapshotVersion = xmlDoc.evaluate(
"(//versions/version[starts-with(., '" + version + "')])[last()]",
xmlDoc, null, XPathResult.STRING_TYPE, null
).stringValue;
// URL zur versionsspezifischen Metadaten-Datei
let metadataXmlURL = "https://central.sonatype.com/repository/maven-snapshots/"
+ e.dataset.mavenGroupid.replaceAll(".", "/")
+ "/" + e.dataset.mavenArtifactid
+ "/" + snapshotVersion + "/maven-metadata.xml";
return metadataXmlURL;
}
Hier werden dieselben Technologien wie in der ersten Funktion verwendet.
(//versions/version[starts-with(., '2025.12')])[last()] werden alle <version>-Elemente gefunden, die mit dem angegebenen Präfix beginnen. Anschließend wird das letzte (neueste) Element ausgewählt.In unserem Szenario war es notwendig, auch Informationen direkt aus der pom.xml-Datei zu ermitteln. Konkret ging es um den Versions-String des verwendeten Parent-Artifacts. Dazu kann man mit den bereits vorgestellten Technologien die POM-Datei parsen.
Allerdings muss im XPath-Ausdruck jetzt mit Namespaces gearbeitet und ein entsprechender Resolver mitgeliefert werden. Dabei handelt es sich um eine einfache JavaScript-Funktion, die zu einem Präfix (hier: mvn:) die Namespace-URL (hier: http://maven.apache.org/POM/4.0.0) zurückliefert.
async function extractMyCoReParentVersionFromPom(url) {
const text = await fetch("https://corsproxy.io/?url=" + url)
.then((response) => response.text());
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, "text/xml");
// Namespace-Resolver für Maven-POMs
const nsResolver = function(prefix) {
const ns = {
mvn: "http://maven.apache.org/POM/4.0.0"
};
return ns[prefix] || null;
};
// Parent-Version extrahieren
const mcrParentVersion = xmlDoc.evaluate(
"/mvn:project/mvn:parent/mvn:version",
xmlDoc, nsResolver, XPathResult.STRING_TYPE, null
).stringValue;
return mcrParentVersion;
}
Die Verwendung eines externen CORS-Proxy-Servers sollte kritisch hinterfragt werden. Hierbei entsteht eine Abhängigkeit von der Verfügbarkeit eines externen Dienstes. Zudem existieren Limitierungen bei der Anzahl der Zugriffe, und letztendlich sollten auch datenschutzrechtliche Aspekte berücksichtigt werden.
Die benötigte Funktionalität lässt sich alternativ mit geringem Konfigurationsaufwand über einen selbstgehosteten Apache2-Webserver bereitstellen. Erste Hinweise dazu finden sich auf der Webseite enable-cors.org.
Mit dieser Lösung haben wir gezeigt, dass sich auch mit einfachen JavaScript-Bordmitteln komplexe Aufgaben lösen lassen. Die Kombination aus Data-Attributen, DOMParser und XPath ermöglicht es uns, dynamische Links auf die neuesten SNAPSHOT-Versionen eines Maven-Artifacts zu generieren – ganz ohne manuelle Pflege der Dokumentation.
Besonders praktisch finden wir, dass die Lösung ohne externe JavaScript-Bibliotheken auskommt und in jedem modernen Browser funktioniert. Auch die Verarbeitung von XML mit Namespaces, wie bei Maven-POMs, stellt kein Problem dar.