Meer met Maven – Build time information at runtime

Deze keer zal ik beschrijven hoe je buildtime informatie beschikbaar kunt krijgen voor een applicatie. Hierbij gebruik ik het versienummer van een project als voorbeeld, maar er zijn veel meer gegevens die je zou kunnen gebruiken.

Een van de standaard locaties waar je dit soort gegevens kan verwachten is de MANIFEST file, welke in de META-INF folder van elk type Java archive gevonden kan worden. Als zo’n archive met Maven gemaakt wordt, dan bevat het standaard een minimale set aan gegevens. Door het aanpassen van de configuratie van de bijbehorende packaging plugin (maven-jar-plugin, maven-war-plugin, etc.) kun je extra gegevens opnemen. Eventueel kun je hier ook eigen entries aan toevoegen. (Zie voor alle mogelijkheden maven.apache.org/shared/maven-archiver/ )

 


<configuration>
 <archive>
  <manifest>
   <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
  </manifest>
 </archive>
</configuration>

 

Met deze toevoeging wordt de MANIFEST file uitgebreid met de verschillende ‘Implementation’entries, waaronder ‘Implementation-Version’.

Met de volgende regels kun je de MANIFEST file benaderen en de inhoud uitlezen:

 


InputStream is = this.getClass().getResourceAsStream( "/META-INF/MANIFEST.MF" );
Manifest manifest = new Manifest( is );
String version = manifest.getMainAttributes().getValue( "Implementation-Version" );

 

In het geval van de het versienummer is er nog een bestand dat gebruikt kan worden. Standaard wordt er tijdens het packagen ook een pom.properties aangemaakt in de /META-INF/maven/${groupId}/${artifactId}/ folder.

 


InputStream is = this.getClass().getResourceAsStream( "/META-INF/maven/nl.nljug.cgi/build-info/pom.properties" );
Properties pomProperties = new Properties();
pomProperties.load( is );
String version = pomProperties.getProperty( "version" )

 

Zowel de MANIFEST-file als de pom.properties worden pas gegenereerd tijdens het packagen. Als je behoefte hebt om al eerder aan dit soort gegevens te komen, bijvoorbeeld voor unittesten, dan kun je gebruik maken van resources.

Daar waar src/main/java alle te compileren source files bevat, is de src/main/resources bedoeld voor alle overige bestanden die ook op het classpath terecht moeten komen.

 


<build>
  <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
  </resource>
</build>

 

De toevoeging ‘filtering’ zorgt ervoor, dat alle expressies ( ${..} ) in het bestand vervangen worden door hun waarde. Filtering staat standaard uit aangezien het een relatief dure operatie is.

Een aantal voorbeelden van expressies:

${project} representeert de effectieve pom.xml, oftewel de huidige pom.xml gecombineerd met al zijn parents.

${env.*}geeft toegang tot alle environment properties

${*}geeft toegang tot alle andere properties, zoals system properties, commandline properties( -Dkey=value) en project properties (<project><properties/> in de pom.xml)

Uit bovenstaande kun je concluderen, dat elk xml-element uit de pom.xml benaderd kan worden. Zou je bijvoorbeeld de sourceDirectory op willen vragen, dan doe je dat met ${project.build.sourceDirectory}.  

Een voorbeeld van zo’n properties-bestand zou er als volgt uit kunnen zien:

 


# pom.xml entry
version = ${project.version}            
# commandline property / project property
bankAccount = ${donate.bankaccount}
# system property
javaRuntimeName = ${java.runtime.name}
# environment property
tempDir = ${env.TEMP}

 

Nadat Maven voorbij de process-resources phase is gekomen,  zijn alle expressies vervangen en kun je dit bestand op dezelfde manier uitlezen als de pom.properties.

 


InputStream is = this.getClass().getResourceAsStream( "/build-info.properties" );
Properties buildinfoProperties = new Properties();
buildinfoProperties.load( is );
String version = buildinfoProperties.getProperty( "version" )

 

Welke manier mijn voorkeur heeft hangt af van de gewenste informatie. Als ik alleen de versie nodig heb, zou ik de pom.properties gebruiken, aangezien dit bestand daar speciaal voor gegenereerd wordt. Bij meer gegevens zou ik vrij snel kiezen voor resources met filtering.