Saturday, February 26, 2011

Getting Groovy with Jar Versions

Oh how I love the Groovy!

I just needed to check version numbers of jar files in multiple folders and collate a table like:

While most jar files have the version number in either the Bundle-Version or Implementation-Version attributes of the MANIFEST.MF file, some do not follow this convention, and we need to either look for a file ending in _VERSION, or parse a pom.xml within a META-INF subfolder to get the version number.

Groovy makes it so easy to do this stuff. On top of this, creating the HTML table is a breeze. The icing on the cake is to save the file and open in Firefox in a couple of lines.


import groovy.xml.MarkupBuilder;
/*
* Given a number of folders, finds all jar files in each folder and retrieves the first version number found using:
* 1. the jar manifest (either Bundle-Version or Implementation-Version)
* 2. a file name ending in _VERSION, stripping the _VERSION suffix
* 3. by parsing a pom.xml file in a subfolder of META-INF and using the text of the version element
* Creates an HTML file containing a table showing the version of each jar file name for each folder.
* Opens the HTML file in Firefox.
*/
def versionFromManifest = { zipFile ->
def versionLines = zipFile.getInputStream('META-INF/MANIFEST.MF').filterLine {
it.startsWith('Bundle-Version') || it.contains('Implementation-Version')
}
def versionLine = versionLines.toString().split('\n')[0]
versionLine ? (versionLine.split(': ')[1]) : ''
}
def versionFromVersionFile = { zipFile ->
def entry = zipFile.entries().find { it.name.endsWith('_VERSION') }
entry ? entry.name - '_VERSION' : ''
}
def versionFromPom = { zipFile ->
def entry = zipFile.entries().find { it.name.matches('META-INF/.*/pom.xml') }
if (entry) {
def pom = zipFile.getInputStream(entry.name).text
def project = new XmlSlurper().parseText(pom)
return project.version
}
}
def jarVersions = [:].withDefault { [:] } // Map of maps [jarFileName: [folderDescription: version]]
def folderDescriptions = []
def addJarVersions = { folderDescription, folder->
folderDescriptions += folderDescription
new File(folder).eachFileMatch(~/.*\.jar/) { f ->
def zip = new java.util.zip.ZipFile(f)
def version = versionFromManifest(zip) ?: versionFromVersionFile(zip) ?: versionFromPom(zip) ?: 'unknown'
jarVersions[f.name][folderDescription] = version
}
}
addJarVersions('ivy', '/local/work/jibx/core/build/ivy/lib')
addJarVersions('lib', '/local/work/jibx/core/lib')
addJarVersions('zip', '/local/java/tools/jibx/jibx-1.2.3/lib')
def writer = new StringWriter()
def doc = new MarkupBuilder(writer)
doc.html() {
head {
style(type:'text/css', new File('table.css').text)
}
body {
table() {
tr {
th()
folderDescriptions.each { folderDescription ->
th(align:'left', folderDescription)
}
}
(jarVersions.sort() *.key).each { jarName ->
tr {
td(style:'font-style:italic', jarName)
folderDescriptions.each { folderDescription ->
td(jarVersions[jarName][folderDescription] ?: 'n/a')
}
}
}
}
p(style:'font-style:italic;font-size:small;float:right', "Results obtained at ${new Date().dateTimeString}")
}
}
def filename = '/tmp/versions.html'
new File(filename).write(writer.toString())
"firefox $filename".execute()

This would have taken hundreds of lines in Java. Groovy makes it concise, while allowing access to all Java libraries, resulting in code that is readable (if a little alien at first) to a Java developer.




1 comment:

John Hurst said...

Nice one Nigel.

Once again, Groovy to the rescue!!!

It truly is the premier scripting solution for the JVM, and much more.

John Hurst