Quantcast
Channel: CodeDependents » java
Viewing all articles
Browse latest Browse all 5

Pimpin' your Ant build with Ivy

$
0
0

As of late Maven has been making headway in the Java build world.  However, there are some who are either in the position of working with an existing system build with Ant or who don’t want to get locked into the Maven file structure.  For those folks there is Ivy to the rescue.

Ivy is an “agile dependancy manager” which recently released 2.0.0-rc1.  Thought jam packed with lovely features the first one that I have put to use is the ability to manage 3rd party dependancies, automagically downloading them, dealing with version conflicts and ensuring that any dependancies they in turn have are dealt with.

Ivy allows for two configuration files, one for describing where to look for jars and one to describe what jars to look for.  Beyond that, the rest of the work is done within your ant build.xml file.  Lets start with the ivysettings.xml file which describes where to look for jars.

<?xml version="1.0" encoding="UTF-8"?>
<ivysettings>  
    <settings defaultResolver="chained"/>  
    <resolvers>  
        <chain name="chained" returnFirst="true">
            <url name="jboss">  
                <artifact pattern="http://repository.jboss.com/maven2/javax/jms/[module]/[revision]/[module]-[revision].jar" />
            </url> 
            <ibiblio name="ibiblio" m2compatible="true"/>
            <filesystem name="local-filesystem">
                <artifact pattern="/local/file/system/[organization]-[revision]/lib/[module].jar" />
                <artifact pattern="/local/file/system/[organization]-[revision]/[module].jar" />
            </filesystem>
        </chain>  
    </resolvers>  
</ivysettings>  

There are a few things to point out here.  First is the resolvers tag. There are a few different type of resolvers and I have chosen to use the chain resolver here.  If you are lucky, everything you need with be in ibiblio and all you would need would be the middle ibiblio tag.  However, if you are using any jars that are not mainstream you will have to end up specifying the url here.  The url tag I’m using here is to deal with the unfortunate issue of jboss not having the latest javax.jms jar up on ibiblio.  Here we specify the url pattern to down load the jar directly as long as the url points at a valid maven style repo.  Notice you can insert the module revision and organization as parameters.  These are passed in from the ivy.xml file which we will discuss next. Also note that in this case there is already a javax.jms jar in ibiblio, just not the right version so we put the url tag first.  Additionally, you might have to deal with some jars that are proprietary or not in a valid maven style repo.  In this case you will have to download these using ant (show later) or store them in source control.  However you can still leverage Ivy by having it pick them up from the local filesystem.

Now onto the ivy.xml file.

<?xml version="1.0" encoding="ISO-8859-1"?>
<ivy-module version="2.0">
    <info organisation="locamoda" module="lms" status="integration" />
    <configurations>
        <conf name="build"  description="build dependancies"/>
        <conf name="runtime" description="runtime dependancies"/>
        <conf name="junit" extends="build, runtime" description="junit dependances"/>
        <conf name="mysql" description="mysql dependances"/>
        <conf name="cobertura" description="cobertura dependances"/>
        <conf name="findbugs" description="findbugs dependances"/>
    </configurations>
    <dependencies defaultconfmapping="*->default">
        <dependency org="com.google.collections" name="google-collections" rev="0.8" conf="build, runtime" />
        <dependency org="log4j" name="log4j" rev="1.2.14" conf="build, runtime" />
        <dependency org="org.apache.activemq" name="activemq-all" rev="5.1.0" conf="build, runtime" />
        <dependency org="commons-lang" name="commons-lang" rev="2.4" conf="build, runtime" />
        <dependency org="commons-dbcp" name="commons-dbcp" rev="1.2.2" conf="build, runtime" />
        <dependency org="org.mortbay.jetty" name="jetty" rev="6.1.11" conf="build, runtime" />
        <dependency org="junit" name="junit" rev="4.5" conf="build" />
        <dependency org="mysql" name="mysql-connector-java" rev="5.1.6" conf="mysql, runtime"/>
        <dependency org="xerces" name="xercesImpl" rev="2.8.1" conf="runtime"/>
        <dependency org="commons-logging" name="commons-logging" rev="1.1.1" conf="runtime" />
        <dependency org="javax.jms" name="jms" rev="1.1" conf="runtime" />
        <dependency org="org.easymock" name="easymock" rev="2.4" conf="junit" />
        <dependency org="org.easymock" name="easymockclassextension" rev="2.4" conf="junit" />
        <dependency org="org.dbunit" name="dbunit" rev="2.3.0" conf="junit" />
        <dependency org="net.sourceforge.cobertura" name="cobertura" rev="1.9rc1" conf="cobertura, junit" />
        <dependency org="findbugs" name="findbugs" rev="1.3.5" conf="findbugs, build" />
    </dependencies>
</ivy-module>
Lets start with the dependencies first.  Each dependancy has an org, name, rev and an optional conf.  The org, name and rev correspond to the organization, module, and revision in the ivysettings.xml as you might assume.  For your common jars you can find the necessary values by looking up the package at mvnrepository.com of course they call org group, module artifact, and revision version, just to keep you on your toes.  Additionally you should notice the defaultconfmapping=”*->default”, if you forget this things blow up quick and in weird ways. The dependancy thing is pretty sweet but the real power comes when you start in on the configurations.  As you can see I have defined six configurations that correspond to different sets of jars that I need to reference in my build.xml.  Configurations can inherit from eachother using the extends so junit is a superset of build, runtime, and the junit specific jars.  Assigning jars to configurations is as easy as listing the right configurations in the conf attribute of the dependancy tag.
Next we tie this all together in the build.xml.
    <target name="dl-binaries" depends="checkfiles, dl-findbugs, dl-ivy" description="download runtime binaries to external"/>

    <target name="checkfiles" description="checks if files have already been downloaded">
        <available file="${findbugs.dir}" type="dir" property="findbugs.exists" />
        <available file="${ivy.jar.file}" property="ivy.exists" />
    </target>

    <target name="dl-findbugs" description="download findbugs" depends="checkfiles" unless="findbugs.exists">
        <get src="${findbugs.url}" dest="${findbugs.zip}" usetimestamp="true" />
        <unzip src="${findbugs.zip}" dest="${external_home}" />
        <delete file="${findbugs.zip}"/>
    </target>

    <target name="dl-ivy" description="download ivy" depends="checkfiles" unless="ivy.exists">
	<mkdir dir="${ivy.jar.dir}"/>
        <get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
            dest="${ivy.jar.file}" usetimestamp="true"/>
    </target>

    <target name="ivy" depends="dl-ivy" description="configure ivy" >
    	<path id="ivy.lib.path">
            <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
        </path>
        <taskdef resource="org/apache/ivy/ant/antlib.xml"
              uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
        <ivy:settings file="ivysettings.xml" />
    	<ivy:retrieve />

        <!-- Define Paths -->
        <ivy:cachepath pathid="build.path" conf="build" />
        <ivy:cachepath pathid="runtime.path" conf="runtime" />
        <ivy:cachepath pathid="junit.path" conf="junit" />
        <ivy:cachepath pathid="cobertura.path" conf="cobertura" />
        <ivy:cachepath pathid="mysql.path" conf="mysql" />
    	<ivy:cachepath pathid="findbugs.path" conf="findbugs" />
    </target>
To start off we have the ivy target which takes care of a few things.  First it depends on dl-ivy for bootstrap downloading of ivy.  Once Ivy is downloaded it sets up the ivy path, defines the ivy ant tasks, and reads the settings file.  At this point all the magic happens with the <ivy:retrieve /> tag.  This goes out, reads the ivy.xml and grabs all the necessary jars.  Once that is done we can create ant paths for all the different configurations we defined in ivy.xml.  This will become super useful later on.  Another thing to notice here is downloading of findbugs.  This is done for two reasons.  First this is a good example of downloading 3rd party libs using ant that are not in a maven style repo.  This target goes out and gets findbugs, unzips it and then ivy can use it through the local filesystem resolver in the ivysettings.xml. The other reason it is here is that the default way of downloading findbugs through the maven style repo conflicts horribly with the findbugs ant plugin.  The plugin expects the files in a very specific layout which is broken when you download from maven vs downloading it from their site.  I use this idiom for downloading some other picky 3rd party jars as well as some binaries that are useful for development but are not actually needed for java.
Finally we get to see the power of these configurations.
     <target name="compile" depends="init,messages,dbobjs" description="compile all the java source">
       <javac target="${TARGET}" source="${SOURCE}" debug="${DEBUG}" destdir="${build}" includes="**/*.java">
            <src path="${src}"/>
            <classpath>
                   <path refid="build.path" />
            </classpath>
            <!-- <compilerarg value="-Xlint"/> -->
        </javac>
    </target>
All we have to do is add the build.path reference and away we go.  We can do the same for findbugs, mysql, junit, adding runtime dependancies into a jar that we create in our build and so on.  Need to add a new jar?  Look it up in the maven repo, add it to ivy.xml and define it as a build, runtime, junit, etc dependancy and away you go.  Pretty slick!


Viewing all articles
Browse latest Browse all 5

Trending Articles