Versionsnamen mit Maven: Auslesen des Versionsnamens

Der erste Artikel in dieser Serie – „Versionsnamen mit Maven: Erzeugen des Versionsnamens“ zeigt, wie mit Hilfe von Maven während des Builds eine Versionsnummer, angereichert mit weiteren Informationen in eine Manifest-, Properties- oder HTML-Datei geschrieben wird. Darauf aufbauend beschreibt dieser Artikel, wie diese innerhalb der Anwendung ausgelesen wird. Dies ist in vielen Fällen hilfreich: Man sieht sofort, welche Version auf welcher Stage deployt ist, die Versionsangabe kann Missverständnisse in Fehlerberichten vermeiden, etc.

Im Folgenden wird der Begriff „Versionsname“ verwendet, um diese mit weiteren Informationen (wie Build-Nummer, Zeitstempel, Branch und Commit ID) angereicherte Versionsnummer zu bezeichnen.

Dieser Artikel zeigt zunächst, wie das programmatische Auslesen des Versionsnamens aus Properties- und Manifest-Dateien mit JDK-Bordmitteln grundlegend funktioniert. Um dies nicht in jeder Anwendung erneut implementieren zu müssen, wurde dies in eine kleine, wiederverwendbare Bibliothek ausgelagert (siehe versionName bei GitHub). Anhand dieser werden im weiteren Verlauf ausführbare Beispiele (Client-Anwendung und Webanwendung, siehe versionName/examples bei GitHub) gezeigt.

Ein programmatischer Zugriff auf den Versionsnamen in der HTML-Datei ist nicht notwendig, da dieser während des Builds direkt in die Datei geschrieben wird und diese direkt auf dem Client angezeigt wird.

Versionsnamen aus einer Properties-Datei auslesen

Die wesentliche Logik für das Auslesen des Versionsnamens aus einer Properties-Datei sieht wie folgt aus:

String getVersionNameFromProperties() throws IOException {
    String propertiesName = "/app.properties";

    InputStream propertiesStream = getClass().getResourceAsStream(propertiesName);
    if (propertiesStream != null) {
        Properties props = new Properties();
        props.load(propertiesStream);

        return props.getProperty("versionName");
    }
    return "";
}

Auf Exception-Handling, Schließen von Ressourcen und Prüfungen auf null wurde in diesem Snippet aus Gründen der Übersicht verzichtet. In der Bibliothek versionName wird dies in der Methode VersionNames.getVersionNameFromProperties() realisiert. Diese gibt defensiv immer eine Instanz vom Typ String (niemals null) zurück. Im Fehlerfall wird ein leerer String zurückgegeben. Fehlermeldungen werden mittels SLF4J geloggt.

Versionsnamen aus dem Manifest auslesen

Analog zum Auslesen der Versionsnamen aus einer Properties-Datei, sieht das Auslesen aus der Manifest-Datei wie folgt aus:

String getVersionNameFromManifest() throws IOException {
    InputStream manifestStream = getClass().getClassLoader().getResourceAsStream("META-INF/MANIFEST.MF");
    if (manifestStream != null) {
        Manifest manifest = new Manifest(manifestStream);
        Attributes attributes = manifest.getMainAttributes();
        return attributes.getValue("versionName");
    }
    return "";
}

Auch in diesem Snippet wurde auf Exception-Handling, etc. aus Gründen der Übersicht verzichtet. In der Bibliothek versionName wird dies in der Methode VersionNames.getVersionNameFromManifest() realisiert. Auch diese gibt defensiv immer eine Instanz vom Typ String (niemals null) zurück und loggt Fehlermeldungen mittels SLF4J.

Verwendung der Bibliothek versionName

Um den Versionsnamen mittels der Bibliothek versionName auslesen zu können, muss diese im Classpath vorhanden sein. Derzeit ist versionName noch nicht im MavenCentral Repository verfügbar. Man kann sie jedoch mittels des JitPack-Repositories, das auf dem Code bei GitHub basiert, verwenden. Dieses spezifiziert man in der pom.xml wie folgt:

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

Damit kann dann die folgende Dependency aufgelöst werden:

<dependency>
    <groupId>com.github.triologygmbh.versionName</groupId>
    <artifactId>versionName</artifactId>
    <version>1.0.0</version>
</dependency>

Alternativ kann man die Klasse VersionNames und die zugehörigen Unit-Tests in das jeweilige Projekt kopieren.

Beispiel Client-Anwendung

Das einfachste ausführbare Beispiel ist eine Client-Anwendung, die in einer JAR-Datei gepackt wird. In dieser kann man den Versionsnamen wie folgt aus der Manifest-Datei auslesen (siehe auch App.java):

public class App {
    public static void main(String[] args) throws IOException {
        System.out.println(VersionNames.getVersionNameFromManifest());
    }
}

Kompiliert man diese Klasse mit Abhängigkeiten in eine JAR-Datei (siehe Beispiel „jar“), kann diese so ausgeführt werden:

java -jar jar/target/jar-1.0.1-SNAPSHOT-jar-with-dependencies.jar

Dies führt beispielsweise zu folgender Ausgabe

1.0.1-SNAPSHOT (2016-10-25T09:51:03Z, branch master, commit 0b2c7b2)

Beispiel Webanwendung

In Webanwendungen bietet es sich an, den Versionsnamen direkt in eine HTML-Datei zu schreiben und im Client anzuzeigen. Damit ist kein programmatischer Zugriff nötig.

Soll der Versionsname auf dem Server verfügbar sein, bietet sich die Verwendung einer Properties-Datei an. Aus dieser kann man den Versionsnamen beispielsweise als JAX-RS-Ressource zur Verfügung stellen (siehe auch VersionResource.java):

@Path("version")
public class VersionResource {
    private static final String VERSION_NAME = VersionNames.getVersionNameFromProperties();

    @GET
    public String getVersion() {
        return VERSION_NAME;
    }
}

Das Beispiel „war“ zeigt eine Webanwendung (die in eine WAR-Datei gepackt wird), die die VersionResource und die zugehörige JAX-RS Application Klasse (siehe RestApplication.java) enthält. Außerdem wird durch den Build der Versionsname in eine HTML- und Properties-Datei geschrieben.

Kompiliert man dieses mit Abhängigkeiten in eine JAR-Datei (siehe Beispiel „server“), die die WAR-Datei auf einem embedded Servlet Container (z.B. Jetty) bereitstellt, kann diese so ausgeführt werden:

java -jar server/target/server-1.0.1-SNAPSHOT-jar-with-dependencies.jar

Nach erfolgreichem Ausführen dieses Befehls gibt sowohl der Aufruf der HTML-Datei unter der URL http://localhost:8080 als auch der Aufruf der JAX-RS-Ressource unter der URL http://localhost:8080/api/version beispielsweise Folgendes zurück:

1.0.1-SNAPSHOT (2016-10-25T09:51:03Z, branch master, commit 0b2c7b2)

Fazit

Diese Artikelserie zeigt im ersten Teil
• wie man den Versionsnamen mit Maven in verschiedene Datei-Typen (Manifest, Properties, HTML) schreibt,
• verschiedene Optionen, mit denen der Versionsname erweitert werden kann (Build-Nummer, Zeitstempel, Branch und Commit ID) ,
und in diesem zweiten Teil
• den generellen Mechanismus zum Auslesen des Versionsnamen aus diesen Dateitypen mit JDK-Bordmitteln,
• die Verwendung der Bibliothek versionName, die eine komfortable Wiederverwendung dieses Mechanismus ermöglicht und
ausführbare Beispiele (Client- und Webanwendung), die all diese Inhalte integrieren.

Diesen Beitrag teilen

Johannes Schnatterer
Solution Architect
Mit besonderem Fokus auf Qualität, Open Source Enthusiasmus, einem Hauch von Pedantismus und der Pfadfinderregel im Gepäck versucht er, die IT-Welt jeden Tag ein winziges Bisschen besser zu machen.