Ivy Dependency Manager

Like most of us often hear, “its another framework or dependency management tool”. But what exactly differentiates Ivy from Maven?

Ivy is much easier to use. The library is simpler since it doesn’t do so much. It only focuses on dependency management and use the already famous and powerful ant to build the application. In short, ant and ivy is used side by side.

Why I Choose Ivy?

The main thing I like about Ivy is its simplicity. You can use Ivy with only ant installed on your system (and of course, java). This means that you don’t need to download any Ivy related libraries or when using eclipse, no plug-in is needed. You can run everything using ant, from installing the ivy library to downloading the dependencies.

Ivy is a dependency management tool that can use other repository such as maven repository.

Installing Ivy

Before continuing, I’ll assume that you are using ant version 1.7. You can choose to run the ant script in either console or from inside eclipse (It depends on your preference).

Here is a build.xml file that contains several targets that will be used in this post:

<?xml version="1.0" encoding="UTF-8"?>

<project name="ivytest" basedir="." default="usage" xmlns:ivy="antlib:org.apache.ivy.ant">

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

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

<path id="run.path.id">
<path refid="lib.path.id" />
<path location="${build.dir}" />
</path>

<property name="ivy.install.version" value="2.0.0-beta1" />
<property name="ivy.jar.dir" value="${basedir}/ivy" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

<target name="usage" description="Displays the command used with this build script">
<echo message="Usage" />
<echo message="-------------------------------------------" />
<echo message="Available targets are:" />
<echo message="download-ivy --> Download ivy library" />
<echo message="install-ivy --> Install ivy library" />
<echo message="clean-libs --> Delete lib files" />
<echo message="clean-ivy --> Delete the ivy files" />
<echo message="resolve --> Resolve/Download ivy dependencies" />
</target>

<target name="download-ivy" unless="skip.download" description="Download ivy.jar">
<mkdir dir="${ivy.jar.dir}"/>
<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>

<target name="install-ivy" depends="download-ivy" description="Install ivy library">
<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="clean-libs" description="Delete all libraries/dependencies">
<echo message="Deleting ${lib.dir}" />
<delete dir="${lib.dir}" />
</target>

<target name="clean-ivy" depends="clean-libs" description="Delete the all folders/files that was generated by ivy">
<echo message="Deleting ${ivy.jar.dir}" />
<delete dir="${ivy.jar.dir}" />
</target>

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

<target name="run" depends="resolve" description="Compile and run the project">
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" />
<java classpathref="run.path.id" classname="Main"/>
</target>

</project>

Invoke “ant install-ivy” and the ivy jars will be downloaded. A new folder named “ivy” will be created with the ivy.jar in it.

Dowloading Dependencies

By default ivy uses maven 2 repository but you can certainly define other repository (It will be covered on later post).

The build.xml above contains a target named “resolve”. Running the target will cause the Ivy to download defined dependencies on ivy.xml (ivy.xml is needed since all the dependencies are defined on that file). Here is an example of ivy.xml:

<?xml version="1.0" encoding="UTF-8"?>

<ivy-module version="2.0">
<info organisation="ideyatech" module="ivytest"/>
<dependencies>
<dependency org="log4j" name="log4j" rev="1.2.8"/>
</dependencies>
</ivy-module>

The code above downloads the log4j and its dependencies from the defined repository (in our case, maven repo). I chose to use log4j because in the next section, I’ll be demonstrating to compile a simple java class that uses a Ivy managed library. Invoke “ant resolve” now and you will see a message saying that the library is being downloaded.

Compiling The Application

Main.java:

import org.apache.log4j.Logger;
public class Main {
    private static final Logger log = Logger.getLogger(Main.class);
    public static void main(String[] args) {
        System.out.println(”Printed using system.out.println”);
        log.info(”Printed using log4j logger”);
     }
}

The sample java file won’t really print the message being logged. The purpose of this example is to test compiling the application with the Ivy libraries being referenced. The idea is to simply add the “lib” dir to the classpath on compilation.

Try invoking “ant run” and the application will be compiled and executed. Note that invoking the “ant clean-libs” and removing the “resolve” dependency on run target will cause the compilation to throw some error.

References:

http://ant.apache.org/ivy/index.html

Note:

  • Ivy logo is property of Apache Ivy and is not connected with Ideyatech.



We’ve been using Continuum for quite some time now. While Continuum does seem to do a good job managing our builds, its limited support for JUnit Test reports made us evaluate an alternative, Hudson. Hudson is the new guy on the block, aside from cool icons and more intuitive interface, this tool seems to be quite competitive and feature-packed.

So, I’m listing down a head-to-head comparison between Continuum and Hudson as we’ve used them and based on features we are looking for:

Build Management:

Continuum seems to perform better in this area. It allows multiple jobs/script configured on a single project. Projects can also be logically grouped. Hudson only supports one job per workspace, which I find very limiting. This means that if we need 3 jobs (e.g. build-war, deploy, run-test), each job needs its own workspace, which doesn’t make sense because all these job share the same source code.

User Management:

I prefer Hudson a little over Continuum because I like simplicity over flexibility. Considering our small team size, there is no point of grouping people with several permissions across multiple projects. All we need is the ability to restrict the developer’s access within their projects. Hudson has a simple, easy-to-use permission system–I was able to setup user access rights within 30 minutes. Continuum seems to be a little over-complicated on this. Continuum also has a strict password policy which makes it more difficult to manage.

Scheduling:

Once again, Hudson is better due to its simplicity. To configure a schedule, simply add “@daily” on schedule option and you’re all set. While Continuum’s scheduling isn’t complex, there’s more clicking involved. Again, Simplicity over Flexibility.

Email Alerts/Notification:

Although both tools provide email support, Hudson has a more detailed email report. Hudson allows the tracking of JUnit test reports for every test, while Continuum only provides notifications on failed or successful builds.

In the end, we decided to switch to Hudson because it is easier to use and it includes the Junit test reporting feature.

Note: The Continuum and Hudson logos are properties of Apache Continuum and Hudson, respectively.

As defined in Wikipedia:

Agile Software Development is a conceptual framework for software development that promotes development iterations, open collaboration, and adaptability throughout the life-cycle of the project.

True to its meaning, Agile is a concept that can be implemented in several ways. So, there is no correct way of getting it right… Nevertheless, here are some tips we’d like to share based on our experience:

Ideyatech agile

  1. Agile team members must be matured and responsible. Team members must be pro-active and have the drive/determination to take charge of the task at hand. Self-organizing teams are hard to develop, especially for junior programmers. It takes some level of maturity and experience for a team member to identify the tasks and execute them.
  2. Agile projects must be contracted as “Time and Material” rather than as “Fix Cost”. This is because agile practice is susceptible to “scope creeps”. The concept of being able to adjust/change requirements during each release indicates that scope must be flexible. As such, contracting on “fix cost” will either limit scope changes or lead to tedious change control processes.
  3. Pair programming should be limited. It is effective only on certain circumstance such as “master-apprentice” pairing or critical components. Otherwise, it will lead to inefficiency where two people are producing half the value of their time.
  4. Tools are essential for unit testing and continuous integration. It is almost impossible to implement unit tests and continuous integration without the proper tools to support the build and test process. It is important that the tool is able to run builds on pre-determined schedules and send configurable alerts. Moreover, choose a tool that can understand the unit test results and do notifications as necessary.
  5. Take the Agile Manifesto with a grain of salt. Not everything in Agile works… it varies on the organization, software and project type. For organizations that are new to this concept, start by shortening your iteration release - at least to monthly. This can take you a long way going agile…

So you wanted a seamless deployment process to your servers without worrying about server specific settings? Just upload the war file and voila!, your production servers have the latest build. Here’s a step-by-step instruction how to achieve this.
STEP 1: Identify all your server variables.

Identify the variables that have different values across the servers such as data file locations and database settings. You will need to externalize these variables so that you don’t have to reconfigure them every time you redeploy. Some of the common variables are:

  • Local location of file/directory.
  • Server tracking variables (e.g. for web analytics)
  • Database access

STEP 2: Externalize your server variables.

The best solution I’ve found to externalize variable is a combination of JNDI and property file. The JNDI identifies which property file to use, while the property file contains the values needed for each server. You’ll need to have several property files, one for each server.

So for example, you have 3 servers that needs access to 3 separate file locations. You will then create 3 property files as follows:
server1.properties containing:

server.dataDir = /usr/home/test/data

server2.properties

server.dataDir = C:/data

server3.properties

server.dataDir = /var/www/html/test/data

Now, set your JNDI (in Tomcat) as follows:

  • Add the following line to server.xml under GlobalNamingResources:

  • Add mapping in your web.xml

  • Add ResourceLink in context.xml.

STEP 3: Add codes to access JNDI and property file.Now that you have the configurations in place, its time to tell your application to read the property files based on the JNDI settings. Create a singleton class that will retrieve the properties as follows:

try {
Context initContext = new InitialContext();
if (initContext == null ) {
throw new Exception(”Boom - No Context”);
}
Context envContext = (Context)initContext.lookup(”java:/comp/env”);
String propFilename = (String) envContext.lookup(”serverProperties”);
Properties prop = new Properties();
try {
prop.load(getClass().getClassLoader().getResourceAsStream(propFilename));
} catch (IOException e) {
e.printStackTrace();
}
… access prop, it now contains the property settings ….
} catch (Exception nex) {
nex.printStackTrace();
}

STEP 4: Test your deployment!

It’s actually that simple to configure your application to be server independent. The other thing you’d like to work on is to create an ant build that will build the war and auto-deploy the war to Tomcat.

After setting up Continuum, I find it reasonably good to support continuous integration (for a free/open-source solution).The package for download is ready to run and easy to configure. This supports Agile development, quick-turn around. Initially, I was skeptic setting up this tool considering it may take me 3-5 days to setup. Well, I was wrong… I was able to get it up and running in a few hours and most of the time was actually spent fixing my ant script.

Deploying Continuum is relatively easy, just extract the tar.gz or zip, and execute bin\run… Viola! You now have continuous integration. :) Well of course, this assumes you have the proper ant scripts and JUnit test codes to support it.

Let me outline the steps needed to implement continuous integration:

1.) Create your JUnit test. It is not much useful to setup continuous integration without unit test, part of the process is to ensure stable and working codes at any point in time (more or less) and the easiest way to achieve this is to run automated test scripts. So, create your unit test first.

2.) Setup your build scripts. The scripts will define the rules and parameters to be executed. You can create “build”, “deploy”, “run-test” scripts or whatever goal you desire to achieve. Personally, I find two main goals necessary - “build” and “run-test”. “build” script performs a clean build and creates the war file for deployment. This will be referenced for testing and promotions. “run-test” executes the unit test scripts so that you can schedule this in your CI tool.

3.) Check-in codes to repo. CI tools retrieve the files from some repository. In case of Continuum, it requires specific SCM URL format to retrieve the source codes for synchronization. This should be pretty simple task as most development environment have some form of repository (SVN, CVS, etc).

4.) Install Continuum. Extract the continuum package and execute the run batch file. You can access it at http:\\localhost:8080\continuum. Considering you have accomplished the tasks above appropriately, getting Continuum to run is quite straightforward as this tool already includes build-in web container(jetty) and database(hsql).

You can read more about continuous integration from Martin Fowler to decide whether this process is a good tool for you.