Managing Dependencies With Ant

Every non-trivial java application have many dependencies on other resources, especially jar files. We have to keep track of which jars we are making use of, and their versions as the project develops. Systems may have several releases during their development life cycle, and among those releases, dependency lists may change, new jars may be added, or old versions may get replaced with newer ones, and we have to trace all those changes as part of our codebase. It is not perceived as a good practice to keep those jars in source control system together with our source codebase. It is preferred to keep those jars in a separate repository, and dependent jars are brought from that location into development environment when required. Hence, we only need to keep dependency information, which jars we depend on, and their versions together with our system’s codebase, and it is used to initialize our development and deployment environment during build or deployment processes.

Maven has important support for dependency management, and in Maven 2.0 it is advanced with transitive dependency, with which you don’t need to discover and specify libraries that our own dependencies require, so for example, it is enough to specify hibernate as our sole dependency, and not libraries that hibernate depends at all. It also brings dependency scope, like compile time, runtime or testing only dependencies above transitive dependency concept.

Maven 2.0 also has dependency tasks for ant as a separate jar, unfortunately it does not work in Turkish locale currently. We starve for such a mechanism in our current project, and a quick search revealed that there are several other solutions similar to Maven’s one. One is Ribomation’s Dependencies Download Ant Task, and the other is Dependencies Ant Task in HttpUnit.org, and both uses Maven’s ibiblio repository as default external repository.

I currently configured second one easily. Concepts are similar to Maven. It manages a set of dependencies, and each missing dependency is downloaded from remote repository, default is maven central repository, and unnecessary repeated downloads are avoided using a local cache. Following lines shortly explain how to install and use dependencies task.

Download the zip archive and extract the ant-dependencies.jar to your ant lib directory, and include typedef in your build.xml file as follows.

<typedef resource="dependencies.properties" />

<dependencies pathId="base.classpath" fileSetId="web-inf.lib" >
    <dependency group="junit"      version="3.8.1" />
    <dependency group="xerces"     version="2.2.1" artifact="xmlParserAPIs"/>
    <dependency group="xerces"     version="2.6.0" artifact="xercesImpl"/>
</dependencies>

Dependencies element defines a set that contains dependency child elements. Each dependency child element defines files, which we require in our project. Group attribute defines external project group which creates dependency. Each project we depend on may be composed of several jars. As a result, any dependency may have an artifact attribute also, to tell which jar file required in specified project group. If project group consists of single jar, no need to add artifact attribute,as it equals to group by default. We also need to specify which version of those jar files our project depends on.

<javac srcdir="${src}" destdir="${classes}">
    <classpath refid="base.classpath" />
</javac>

<copy todir="${web-inf}/lib" flatten="true">
    <fileset refid="web-inf.lib"/>
</copy>

Missing jars are downloaded into local cache, which is ~/.maven/repository by default. If you want to change it, you must set ant.dependencies.cache system property before running ant. Please, be careful that, it is not normal ant property, it is java system property, so you should give it into JRE as a parameter with –D option. We can also change default remote repository, which is ibiblio, by setting repositoryList attribute of dependency task. It takes comma separated HTTP URLs as remote repository values. Another way to change external repository is to use ant.remote.repository system property, but it is deprecated.

You may want create your own external repository, in your intranet, and eliminate the need for connecting a remote repository over Internet. If you examine maven central repository  repository, you will see that it is enough to create a directory structure, each project group having its own folder same as the group name, and jars folder in each group folder to keep jar files, and each jar file is in some form of <jarFileName>-<x.y.z>, e.g. junit-3.8.1.jar. You don’t have to configure web servers to create your own external repository either. It is valid to provide some shared network folder in form of URI, e.g. file:///tbsserver/intra-jar-repository.