Meer met Maven – Merging complex plugin configuration

In elke editie zal Robert Scholte een probleem voorleggen en deze oplossen met behulp van Apache Maven om meer inzicht te geven in Maven zelf en de vele beschikbare plugins.

Eén van de mogelijkheden van Maven projecten is het overerven van projecteigenschappen via parent(s). Daarmee kan ik bijvoorbeeld gebruik maken van een standaard configuratie voor een plugin welke ik kan aanvullen of overschrijven in mijn eigen project. In veel gevallen zal de configuratie overschreven worden, maar wat gebeurt er bij Lists of Maps? En kan ik dit gedrag aanpassen?

Hierover heeft Benjamin Bentmann, de developer van o.a.  Maven’s dependency management implementatie Aether,  in het verleden een blog geschreven met de titel “Maven How-To: Merging Plugin Configuration in Complex Projects”, wat ik gebruik als input voor dit artikel.

Stel dat ik een parent POM heb, waarbij een plugin de volgende configuratie bevat:


<configuration>
  <items> <!-- java.util.List -->
    <item>parent-1</item>
    <item>parent-2</item>
  </items>
  <properties> <!-- java.util.Map -->
    <parentKey>parent</parentKey>
  </properties>
</configuration>

Daarnaast heb ik een project dat bovenstaande parent POM gebruikt, maar waarbij dezelfde plugin als volgt is geconfigureerd:


<configuration>
  <items>
    <item>child-1</item>
  </items>
  <properties>
    <childKey>child</childKey>
  </properties>
</configuration>

Zoals bekend zal met Maven deze configuratie gecombineerd worden. Voor een List geldt dat het de waarden van de parent vervangt, terwijl bij een Map de waarden worden aangevuld. Het resultaat van de effectieve plugin configuratie zal er dan ook als volgt uit zien:


<configuration>
  <items>
    <item>child-1</item>
  </items>
  <properties>
    <childKey>child</childKey>
    <parentKey>parent</parentKey>
  </properties>
</configuration>

In sommige gevallen is dit niet het gewenste resultaat. Stel dat ik in plaats van het overschrijven van items deze juist wil aanvullen. Of dat ik bijvoorbeeld alle properties wil vervangen. Binnen de plugin configuratie kan ik de volgende attributen toevoegen om het gedrag te wijzigen:


<configuration>
  <items combine.children="append">
    <!-- combine.children="merge" is the default -->
    <item>child-1</item>
  </items>
  <properties combine.self="override">
    <!-- combine.self="merge" is the default -->
    <childKey>child</childKey>
  </properties>
</configuration>

Als ik nu nogmaals de effectieve plugin configuratie opvraag, krijg ik het volgende resultaat:


<configuration>
  <items combine.children="append">
    <item>parent-1</item>
    <item>parent-2</item>
    <item>child-1</item>
  </items>
  <properties combine.self="override">
    <childKey>child</childKey>
  </properties>
</configuration>

Het is dus mogelijk om met de attributen combine.children en combine.self het standaard gedrag van Maps en Lists aan te passen.

Voor alle duidelijkheid: deze combine.* attributen werken alleen binnen de <configuration/>-tags van een plugin en niet op andere elementen binnen de pom.xml .

Deze techniek werkt niet alleen voor Maven 3.x, maar wordt ook al ondersteund door Maven 2.x .

Het standaard gedrag voor Maps en Lists heeft vaak de voorkeur, maar voor de uitzonderingen heeft Maven wel een oplossing. Pas erg op met het gebruik van deze attributen in parent-poms, aangezien het van invloed zal zijn op de hele configuratie -hiërarchie en daarmee verrassende effecten kan geven.

Tot slot is er nog 1 magisch attribuut, welke pas sinds Maven 3.1.0 wordt ondersteund: xml:space=”preserve”. Standaard wordt de tekst tussen tags getrimmed, maar met deze attribuut + waarde behoudt de tekst alle whitespace characters.