Friday, 4 December 2009

Apache Ivy - Working with the Apache tutorial

Apache Ivy is a dependancy manager and appears to be flavour of the month in some circles and claims to be less complicated than Maven.

Here is a quick tutorial that expands on Apaches tutorial page. They provide one very small build.xml in a style that they say is not best practise for a number of reasons:
  1. it is standalone
  2. it contains the code within the build.xml
  3. it downloads ivy locally instead of using a remote installation
I struggled to find a simple example of a build in the accepted syntax so I will attempt here to describe how this build.xml can be changed in 4 steps to ultimately work in the conventional style with a build.xml and ivy.xml files and a remote installation of ivy.

My next project will be to 'ivyfy' a spring project. Hopefully I will be successfull - watch this space.

prerequisites:
Apache Ant installed to 1.6 or greater

1) The basic Tutorial from Apache Ivy
  • Essentially follow their instructions & download the build.xml from the tutorial page
  • I had to edit the downloaded file to remove the dashes at the start of some lines
  • run 'ant'
  • I also changed the following line to refer to the latest formal release of ivy, but both will work.
from:
 <property name="ivy.install.version" value="2.0.0-beta1" /> 
to:
    <property name="ivy.install.version" value="2.1.0" />

2) Tutorial with no ivy
  • One of things about ivy is that you are supposed to take an existing project and 'ivyfy' - IE remove your local third party jars and rely on ivy to fullfill your dependancies
  • So this tutorial simply takes the results from the previous example and gets it to work without ivy.
  • It therefore needs the src folder & its contents, the commons-lang jar in a lib dir in the project and a build.xml
  • The file structure for this and future tutorials is:
.
./build.xml
./file
./filestructure
./lib
./lib/commons-lang-2.1.jar
./src
./src/example
./src/example/Hello.java
  • The build.xml required is:
<project name="go-ivy" default="go" >

<property name="build.dir" value="build" />
<property name="src.dir" value="src" />
<property name="lib.path.id" value="lib" />

<path id="compile.classpath">
<fileset dir="${lib.path.id}">
<include name="**/*.jar"/>
</fileset>
</path>

<!-- =================================
target: go Go ivy, go!
================================= -->
<target name="go" depends=""
description="--> resolve dependencies, compile and run the project">
<echo message="NOT using ivy to resolve commons-lang 2.1..."/>

<echo message="compiling..."/>
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}" destdir="${build.dir}" >
<classpath refid="compile.classpath"/>
</javac>

<java classname="example.Hello">
<classpath>
<fileset dir="${lib.path.id}">
<include name="**/*.jar"/>
</fileset>
<path location="${build.dir}" />
</classpath>
</java>
</target>


<!-- =================================
target: clean
================================= -->
<target name="clean" description="--> clean the project">
<delete includeemptydirs="true" quiet="true">
<fileset dir="${src.dir}" />
<fileset dir="${build.dir}" />
</delete>
</target>
</project>

3) Tutorial with ivy.xml
  • In the above example everything is defined in the build.xml. The clever thing with ivy and maven is that your project does not explicitly contain third party jars.
  • Instead these are defined in a config file, ivy.xml in this case.
  • This step moves the dependancies defined in build.xml into a seperate ivy.xml
  • again simply run 'ant' to test.
  • For this and subsequent tutorials I did not generate the src files. I kept them from the first tutorial
  • Therefore for this and subsequent tutorials all you need is the src folder with the src file, the build.xml & ivy.xml
  • Here is the ivy.xml
<ivy-module version="2.0">
<info organisation="Utilisoft" module="Test-withivy.xml"/>
<dependencies>
<dependency org="commons-lang" name="commons-lang" rev="2.1"/>
</dependencies>
</ivy-module>
  • Here is the new build.xml - I have removed much of the comments to make it smaller.
<project name="go-ivy-with-ivy.xml" default="go" xmlns:ivy="antlib:org.apache.ivy.ant" basedir=".">
<!-- here is the version of ivy we will use. change this property to try a newer
version if you want -->
<property name="ivy.install.version" value="2.1.0" />
<property name="ivy.jar.dir" value="${basedir}/ivy" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

<property name = "ivy.lib.dir" value="${basedir}\newlib" />

<path id="lib.path.id">
<fileset dir="${ivy.lib.dir}" />
</path>

<property name="build.dir" value="build" />
<property name="src.dir" value="src" />

<!-- =================================
target: go Go ivy, go!
================================= -->
<target name="go" depends="resolve "
description="--> resolve dependencies, compile and run the project">
<echo message="using ivy to resolve commons-lang 2.1 with ivy.xml"/>

<echo message="compiling..."/>
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" />

<java classname="example.Hello">
<classpath>
<path refid="lib.path.id" />
<path location="${build.dir}" />
</classpath>
</java>
</target>


<!-- =================================
target: clean
================================= -->
<target name="clean" description="--> clean the project">
<delete includeemptydirs="true" quiet="true">
<fileset dir="${src.dir}" />
<fileset dir="${build.dir}" />
</delete>
</target>

<target name="resolve" depends="install-ivy" description="Resolve the dependencies">
<ivy:retrieve/>
</target>


<!-- =================================
target: install-ivy
this target is not necessary if you put ivy.jar in your ant lib directory
if you already have ivy in your ant lib, you can simply remove this
target and the dependency the 'go' target has on it
================================= -->
<target name="install-ivy" depends="download-ivy" description="--> install ivy">
<!-- try to load ivy here from local ivy dir, in case the user has not already dropped
it into ant's lib dir (note that the latter copy will always take precedence).
We will not fail as long as local lib dir exists (it may be empty) and
ivy is in at least one of ant's lib dir or the local lib dir. -->
<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"/>
</target>

<target name="download-ivy" unless="skip.download">
<mkdir dir="${ivy.jar.dir}"/>
<!-- download Ivy from web site so that it can be used even without any special installation -->
<echo message="installing ivy..."/>
<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>
</project>

4) Tutorial with remote Ivy
  • Hopefully you are still with me. This is the last step in this tutorial set.
  • The above example still downloads ivy locally
  • This example will now use ivy as part of your apache and installation
  • In the last example a ivy.jar file appeared in an 'ivy' directory.
  • Determine the location of your apache ant installation and copy this jar into the apache ant lib directory.
  • Ensure you have an ANT_HOME environment variable pointing to the apache4 ant installation directory
  • Below is the new build.xml file you will need.
<project name="go-ivy-with-ivy.xml" default="go" xmlns:ivy="antlib:org.apache.ivy.ant" basedir=".">
<!-- here is the version of ivy we will use. change this property to try a newer
version if you want -->
<property name="ivy.install.version" value="2.1.0" />
<property name="ivy.jar.dir" value="${basedir}/ivy" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

<property name = "ivy.lib.dir" value="${basedir}\newlib" />

<path id="lib.path.id">
<fileset dir="${ivy.lib.dir}" />
</path>

<property name="build.dir" value="build" />
<property name="src.dir" value="src" />

<!-- =================================
target: go
Go ivy, go!
================================= -->
<target name="go" depends="resolve "
description="--> resolve dependencies, compile and run the project">
<echo message="using ivy to resolve commons-lang 2.1 with ivy.xml"/>

<echo message="compiling..."/>
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" />

<echo>
We are now ready to execute our simple program with its dependency on commons-lang.
Let's go!
</echo>
<java classname="example.Hello">
<classpath>
<path refid="lib.path.id" />
<path location="${build.dir}" />
</classpath>
</java>
</target>

<!-- =================================
target: clean
================================= -->
<target name="clean" description="--> clean the project">
<delete includeemptydirs="true" quiet="true">
<fileset dir="${src.dir}" />
<fileset dir="${build.dir}" />
</delete>
</target>

<target name="resolve" depends="" description="Resolve the dependencies">
<ivy:retrieve/>
</target>
</project>

And to the future. I have two immediate aims:
  1. Get a build of a spring hibernate using Ivy
  2. Get a project with dependant projects using spring and hibernate using ivy.

3 comments:

ekangas81 said...

Thanks for the article. I've been having trouble with the Ivy tutorials because they don't provide enough visual aids or work-through tutorials. Your tutorial has substantially helped my understanding of how Ivy works. I've been doing Ivy/Maven research for work, and we're attempting to decide whether it's worth it to make the change to Ivy from Ant with jars in our SVN repositories.

Eric

Anonymous said...

Many Thanks for this great article. Your tutorial has helped me a lot to understand how Ivy works in less than 10 minutes.

Anonymous said...

Thank you so very much for the detailed write-up. This cleared up Apache Ivy for me quite precisely.