Category Archives: XSLT

Changing Maven POM Versions With Groovy and XSLT

One of the projects I am working on uses Apache Maven for doing builds. Usually, I let the Maven Release plugin take care of all of the version numbering. When I instruct maven to generate a release version or do a branch, it will ask for the new version for each of the artifacts in my project. The project I’m working on has about 15 different artifacts so switching the version numbers all of the time can take a bit of time. Allowing Maven to do this for me makes sense.

This brings me to my latest problem. We needed to change the version of all of our Maven artifacts outside of a release. Instead of incrementing the minor release number we are preparing for a major release (1.x to 2.x). I could do the version change the brute force way but being a programmer, I have strong instincts that prevent me from doing a repeatable monotonous task. Am I lazy? A bit, but I like to call it being efficient. I would rather take an hour and write a small script to do the task for me than take 15 minutes on the brute force approach.

What I needed to do was change all of the occurrences of 1.0-SNAPSHOT to 2.0-SNAPSHOT in all of my pom.xml files. Not only does this need to change for the main artifact but also for any dependencies.


  <groupId>com.chrisdail.project</groupId>
  <artifactId>parent</artifactId>
  <version>1.0-SNAPSHOT</version>

My first attempt to write a script to do this was using Python. There were a few issues I ran into pretty quickly. I do not like most of the standard python libraries for XML parsing. The DOM library has the same issues as the Java DOM library. I just wanted a ‘simple’ XML library. So I decided to use the ElementTree library. I have used it in the past and it is very nice to use. I soon found that this would give me a few problems.

  • ElementTree does not support namespaces well. I wanted to use the findAll() method but it does not support namespaces.
  • Another thing I wanted to do is use full XPath. It is very simple to express finding all versions where the ../groupId = ‘mygroup’ in xpath. XPath seems better than writing this in code

Because of these issues I quickly decided that Groovy may be a better language for this script. Groovy has great XML support using their XmlParser. I actually produced a fully working script using the XmlParser that did exactly what I needed. The only issue with it was that when you write out the XML from Groovy after it was parsed, it was not maintaining the original formatting. I did not want to have the entire formatting of my pom.xml files be changed just for a version change. So this lead me back to XSLT.

I had done a lot of XSLT in my previous job and it really is the best tool for this kind of XML manipulations. It is very easy to preserve the original XML structure and make small modifications to it. The approach is to start with the standard identify stylesheet. This stylesheet essentially produces an exact copy of the original. From there you can add specializations to modify any things you need. Here is the identity stylesheet:


<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

To this, I added a template to match the maven version number element where the ../groupId was the group I was concerned with and change the version number to what I desired.


    <xsl:template match="m:version[../m:groupId='com.chrisdail.project']">
		<version>2.0-SNAPSHOT</version>
    </xsl:template>

This 10 line XSLT did all of the processing on the pom.xml file I required. I wrapped this in a groovy script to walk the file tree and run it against all pom.xml files. This is the final script I came up with. It walks the tree for pom.xml files. For each file it finds any groups of com.chrisdail.project and replaces the version number with my new version number of 2.0.


import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource

def xslt = '''
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:m="http://maven.apache.org/POM/4.0.0" exclude-result-prefixes="m">
    <xsl:template match="m:version[../m:groupId='com.chrisdail.project']">
		<version>2.0-SNAPSHOT</version>
    </xsl:template>
    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
'''.trim()

def processFile = {
    def file = new File(it, "pom.xml");
    if (file.isFile()) {
        println "Processing: $file"

        def writer = new StringWriter()
        def factory = TransformerFactory.newInstance()
        def transformer = factory.newTransformer(new StreamSource(new StringReader(xslt)))
        transformer.transform(new StreamSource(new FileReader(file)), new StreamResult(writer))

        file.write(writer.toString())
    }
}

file = new File("/path/to/root/dir")
processFile(file)
file.eachDirRecurse(processFile)

It did take a bit more time than changing all of the pom.xml files manually but it was much more satisfying.