To keep track of a version number, we used to have it in two files: once in a Constants.java for the application itself, and once in the Ant build script in order to have a version number in the resulted WAR's META-INF section. This duplication was cumbersome and error-prone, we always had to update both files and make sure the version numbers matched; in a distributed develop/build environment, this was a nighmare.
Solution: We decided to stick to one version number, in the Constants.java file and to create a custom Ant taskdef to retrieve this number from the compiled class file at the end of the building process.
We have the following parts:
- in our build.xml, the following taskdef:
<taskdef name="versionPropertyRetriever" classname="VersionPropertyRetriever" classpath="${basedir}"/>
and the following task call
<versionPropertyRetriever name="app.version" className="com.datamat.care.base.Constants" classpath="${basedir}; ${build.web.home}/WEB-INF/classes"/>
which retrieves the version number and makes it available as the app.version property. - a VersionPropertyRetriever java class, extending Ant's Property.java, whose main purpose is to retrieve the value of the VERSION constant in the className class through introspection. Besides adding a getter and setter for the className attribute, its main business logic is implemented within the private parseClass method:
private void parseClass() {
try {
ClassLoader cL = getProject().createClassLoader(classpath);
Class c = cL.loadClass(className);
if (c==null)
throw new BuildException("Couldn't find the Constants class");
Field field = c.getField("VERSION");
if (field==null)
throw new BuildException("'VERSION' field not found within Constants.java file");
Object value = field.get(null);
if (value==null)
throw new BuildException("Version not set within Constants.java file");
// all fine -> add as a property
addProperty(name,value.toString());
}
catch(Exception e) {
throw new BuildException("Unexpected Exception: " + e.getMessage());
}
}
We compiled the VersionPropertyRetriever.java file through another xml file, to be executed with Ant, buildVersionPropertyClass.xml:
<!--
Script to compile the VersionProperty class starting from the VersionProperty.java file
-->
<project name="buildVersionPropertyClass" default="main" basedir="./">
<property file="build.properties"/>
<path id="compile.classpath">
<!-- Include all JAR files needed to compile -->
<fileset dir="${basedir}" includes="*.class"/>
<fileset dir="${libraries.home}/Ant" includes="*.jar"/>
</path>
<target name="main" depends="" description="Compile java file">
<echo message="Compiling"/>
<javac srcdir="${basedir}" destdir="${basedir}" includes="*.java">
<classpath refid="compile.classpath"/>
</javac>
</target>
</project>
