I really love Maven, but sometimes building a Java project isn't easy when the project and it's ant scripts have been around for a while. Converting to Maven is not an easy task in this case, but Ivy makes this really simple...once you figure out how to use it.
Just in case you need to be sold on the benefits of using Ivy, here are some key reasons.
1. With Ivy it is no longer necessary to store jars in version control.
2. With Ivy you can upgrade a jarby simply changing the xml
3. Ivy can manage which jars are included and excluded in a war or ear file
4. Ivy can utilize any Maven repository for building
Need I go on...
Ivy is a dependency management library that integrates with Ant. It supports Ivy repositories and Maven repositories. All you need to get started is the Ivy jar in your ANT_HOME/lib, an ivy.xml and a few lines in your build.xml. Lets take a look at an ivy.xml first.
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:e="http://ant.apache.org/ivy/extra"
xsi:noNamespaceSchemaLocation="ivy.xsd">
<info organisation="Mycompany" module="myProject" status="integration" revision="1.0-SNAPSHOT"/>
<configurations>
<conf name="build" />
<conf name="war" />
</configurations>
<publications>
<artifact name="myproject-web" type="jar" ext="jar"/>
<artifact name="myproject-web" type="war" ext="war"/>
<artifact name="myproject-web" type="source" ext="jar" e:classifier="source"/>
</publications>
<dependencies>
<!-- This jar will be included in the war -->
<dependency org="oracle.sql" name="ojdbc14" rev="10.2.0" transitive="false" conf="war->default" />
<!-- Testing Only Libs this jar is excluded from the war -->
<dependency org="junit" name="junit" rev="4.7" transitive="false" conf="build->default"/>
<dependency changing="true" conf="war->default" name="my-common" org="my" rev="2.0-SNAPSHOT" transitive="false">
<artifact ext="jar" name="my-common" type="jar">
<artifact e:classifier="source" ext="jar" name="my-common" type="source">
</artifact>
...
The ivy.xml is pretty straightforward. This one contains many different examples. Ivy is very flexible. The example if for bulding and publishing a war, but you can remove all the configuration options from the dependencies if you are building a single jar.
Now we need the build.xml. This is a bit overkill, but it has all the options you'll need. The ivy:cachepath tag allows you to build your classpath from your Ivy cache as opposed to copying jars to a local directory and building from there. You only need to call ivy:retrieve in the event that you need to copy jars into the WEB-INF/lib like in the example below. In a jar project, you won't call retrieve at all. ivy:resolve will determine the location of all dependencies in the ivy.xml and download them to the cache if they are not already there. The publish will copy a finished product to your local ivy repository.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:ivy="antlib:org.apache.ivy.ant"
xmlns:artifact="antlib:org.apache.maven.artifact.ant"
xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"
xmlns:contrib="antlib:net.sf.antcontrib"
name="my-onelinkweb"
default="build-stage"
basedir=".">
<property name="compile.debug" value="true"/>
<property name="compile.optimize" value="false"/>
<property name="version" value="1.0-SNAPSHOT"/>
<property name="jarfilename" value="${ant.project.name}-${version}.jar"/>
<property name="jarfilename-src" value="${ant.project.name}-${version}-source.jar"/>
...
<ivy:settings url="http://maven/maven/ivysettings.xml"/>
<ivy:resolve checkifchanged="true" refresh="true" />
<ivy:cachepath log="download-only" type="jar" pathid="build.project.classpath" conf="build" />
<ivy:cachepath log="download-only" type="jar" pathid="war.project.classpath" conf="war" />
<target name="retrieve" description="--> retrieve dependencies with ivy">
<ivy:retrieve sync="true" conf="war" pattern="${basedir}/WebContent/WEB-INF/lib/[artifact]-[revision](-[classifier]).[ext]" type="jar,source" />
</target>
<target name="publish" >
<ivy:publish pubrevision="${version}"
resolver="local" forcedeliver="true"
update="true" overwrite="true" publishivy="true" status="integration" >
<artifacts pattern="${basedir}/build/[artifact]-[revision](-[classifier]).[ext]" />
</ivy:publish>
<artifact:install file="build/${jarfilename}">
<pom refid="pom"/>
<attach file="${basedir}/build/${jarfilename-src}" classifier="source" />
</artifact:install>
<artifact:install file="${war.location}">
<pom refid="pom"/>
</artifact:install>
</target>
<target name="archive" >
<jar jarfile="build/${jarfilename}" whenempty="fail">
<fileset dir="build/classes"></fileset>
<fileset dir="${classes.dir}" includes="com/**" ></fileset>
</jar>
<jar jarfile="build/${jarfilename-src}" whenempty="fail">
<fileset dir="src">
</fileset>
</jar>
</target>
<target name="compile" >
<mkdir dir="${build}" />
<javac source="1.5" target="1.5" srcdir="${src.dir}" destdir="build/classes" debug="${compile.debug}" optimize="${compile.optimize}">
<classpath refid="build.project.classpath"/>
<classpath refid="war.project.classpath"/>
</javac>
<copy todir="build/classes" filtering="true">
<fileset dir="WebContent/WEB-INF/classes" includes="*.properties,ehcache*.xml"/>
<fileset dir="${src.dir}" includes="*.properties,ehcache*.xml"/>
<fileset dir="${dest.dir}" includes="log4j.xml"/>
</copy>
</target>
<path id="test.classpath">
<pathelement location="build/classes" />
<pathelement location="WebContent/WEB-INF" />
<pathelement location="src" />
</path>
Again, here we see Ivy is extremely flexible. In this example, we also specify an ivysettings.xml file. I highly recommend that you create a Maven and Ivy repository in your enterprise somewhere and manage it with Nexus Dependency management server and point your ivy settings file to that Nexus server. If you choose this path, here is an example ivysettings.xml to get your started. In this example, the internal repositories are first in the chain and then public repositories follow. If you use a custom settings file, put it on a Web server somewhere so that everyone will have access to it.
<?xml version="1.0" encoding="UTF-8"?>
<ivysettings>
<settings defaultResolver="internalChain"/>
<property name="local-maven2-pattern" value="${user.home}/.m2/repository/[organisation]/[module]/[revision]/[module]-[revision](-[classifier]).[ext]"/>
<include url="${ivy.default.settings.dir}/ivysettings-public.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
<include url="${ivy.default.settings.dir}/ivysettings-default-chain.xml"/>
<property name="ivy.local.default.root" value="${ivy.default.ivy.user.dir}/local" override="false"/>
<property name="ivy.local.default.ivy.pattern" value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false"/>
<property name="ivy.local.default.artifact.pattern" value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false"/>
<resolvers changingPattern=".*-SNAPSHOT">
<filesystem name="local-ivy" checkmodified="true">
<ivy pattern="${ivy.local.default.root}/${ivy.local.default.ivy.pattern}"/>
<artifact pattern="${ivy.local.default.root}/${ivy.local.default.artifact.pattern}"/>
</filesystem>
<filesystem name="local-maven-2" m2compatible="true" local="true" checkmodified="true">
<ivy pattern="${local-maven2-pattern}"/>
<artifact pattern="${local-maven2-pattern}"/>
</filesystem>
<ibiblio name="nexus" root="http://nexus/content/groups/Company/" m2compatible="true" checkmodified="true"/>
<ibiblio name="nexus2" root="http://nexus2/nexus/content/groups/Company/" m2compatible="true" checkmodified="true"/>
<ibiblio name="internal" root="http://maven" m2compatible="true"/>
<ibiblio name="internal2" root="http://maven2" m2compatible="true"/>
<ibiblio name="m2repo" m2compatible="true"/>
<chain name="internalChain" checkmodified="true" returnFirst="false">
<resolver ref="local-ivy"/>
<resolver ref="local-maven-2"/>
<resolver ref="nexus"/>
<resolver ref="nexus2"/>
<resolver ref="internal"/>
<resolver ref="internal2"/>
</chain>
<chain name="chained" returnFirst="true"> <!-- dual="false" checkmodified="true" -->
<resolver ref="local-ivy"/>
<resolver ref="local-maven-2"/>
<resolver ref="nexus"/>
<resolver ref="internal"/>
<resolver ref="nexus2"/>
<resolver ref="m2repo"/>
</chain>
</resolvers>
</ivysettings>
Another useful trick when using Ivy is to use the Eclipse Ivy plugin to allow Eclipse to build and run using the Ivy.xml. Using the Ivy plugin isn't simple, but it once you figure it out, you'll wonder how you ever lived without it. You might even prefer Ivy over Maven for the enhanced flexibility it adds to an existing Ant project.
Monday, December 21, 2009
Subscribe to:
Post Comments (Atom)

4 comments:
I strongly agree with CH. If you have a large existing ant build, making the move to externalized dependency management is far easier with ivy than trying to do a conversion to, for instance, maven.
There exists intrinsic value in moving to maven, but if it's impossible or at least out of scale, ivy makes a great middle-ground while generally integrating easily.
I wholeheartedly agree that at times Maven can be more trouble than it is worth. But what if you can have the best of both worlds, i.e. use Ant for the heavy lifting when necessary - but at the same time being 'Maven compliant':
http://ptrthomas.wordpress.com/2009/03/08/why-you-should-use-the-maven-ant-tasks-instead-of-maven-or-ivy
And I have some reservations about Ivy, see towards the end of my blog post for details.
Have you considered moving to Gradle ? You will get best of both worlds: Ant/Ivy and Maven.
--
Tomek Kaczanowski
http://kaczanowscy.pl
You may also want to give Artifactory a try as an Ivy repo manager, as it has Ivy (and Gradle ;) specific features, such as Ivy dependency snippets from POMs, deploy to arbitrary paths from the UI (with no Maven layout limits) and searching inside your Ivy modules.
Post a Comment