Posts Tagged Ant

Adding Ivy to the Ant Boilerplate

I have wanted to delve deeper into Ivy for a while now, but something always pulled me away before I got very far into it. Well today I had some time, so I did a little digging.

Ivy is a dependency management system along similar lines to that provided by Maven but without all of Maven’s other features. Ivy just does dependency management. Ivy uses a project (module) description file for project configuration, generally named ivy.xml and put in the root of your project. Then you can add additional ant tasks to manage the dependencies.

Dependency management puts the common jars you (and everyone else) use in a common repository so that you can have quick standardized access to the jar and version that you need for each project. Ivy knows how to find these based on the configuration of your dependencies in the ivy.xml file. The dependencies are then cached locally for your use and added to projects as required.

A simple ivy.xml file is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<ivy-module version="2.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
               
    <info organisation="com.stehno" module="foobar" />
   
    <configurations>
        <conf name="prod" description="Production environment conf."/>
        <conf name="dev" transitive="false" description="Testing environment conf."/>
    </configurations>
   
    <dependencies>
        <!-- For Production -->
     
        <dependency org="org.springframework" name="spring-webmvc" rev="2.5.6" conf="prod->default">
            <exclude org="commons-attributes"/>
            <exclude org="commons-digester" />
            <exclude org="jexcelapi" module="jxl"/>
            <exclude org="velocity" />
            <exclude org="org.apache.poi" />
            <exclude org="struts" />
            <exclude org="org.apache.tiles" />
            <exclude module="freemarker" />
            <exclude module="xml-apis" />
            <exclude module="jasperreports" />
            <exclude module="velocity-tools-generic" />
            <exclude module="velocity-tools-view" />
            <exclude module="itext" />
            <exclude module="jfreechart" />
            <exclude module="jcommon" />
        </dependency>
       
        <dependency org="commons-lang" name="commons-lang" rev="2.4" conf="prod->default"/>
       
        <!-- For Development -->
     
        <dependency org="junit" name="junit" rev="4.4" conf="dev->default"/>
       
        <dependency org="org.springframework" name="spring-test" rev="2.5.6" conf="dev->default" />
    </dependencies>
</ivy-module>

You see basic module information (info element) which is used to identify your project. The configurations define various configurations available. In this case I have one prod and one dev configuration so that I can define one set of dependencies that will be pushed out with the production artifact and one that is only used locally for testing and development. The dependencies are defined in dependency elements using the standard Maven repository conventions, the conf attribute being a reference to which configuration the dependency belongs to. The ->default is something I am not totally clear on, but I think it means that the dependency will also be associated with the default configuration.

One downside I have noticed about Ivy is that when it pulls down dependencies, it pulls them all down without prejudice. So when you have something like Spring which has a lot of non-required dependencies, you get them all and have to exclude those you don’t want (as I did in the sample with the exclude elements). It’s not horrible but you have to do it by hand. If you don’t really care about the size of your application you can just forget about it and let it pull down everything. Something missing along these lines is a general exclusion that would exclude a dependent jar from any module. The way it stands, if module A and module B both depend on the module C, which is not required, you will have to exclude it from both dependency definitions.

The dependency management strategy that I am going with here is one that I hope will “stay out of my way”. I was thinking about when dependency resolution is really needed. Maven checks for dependency changes whenever you do anything (at least pre-2.0 did, I am not sure about 2.0) so that even running clean caused a dependency check… how wasteful is that? In my opinion, dependency management should be done when you want it done. When is it relevant?

  • when you add or remove a dependency
  • when you change a dependency version

How often do these events really happen? In a structured working environment these events often require buy-in from other developers and/or managers. Even at home on your own projects, they only happen when you feel the need to change one… not every time you run your build script. On a related note, IDEs tend to get cranky when you keep adding/removing jars out from under them too.

This leads me to add three new targets to my Boilerplate Ant File:

The depends target to update the local dependencies in your project.

1
2
3
4
<target name="depends" description="Update the dependencies for the project.">
    <ivy:retrieve sync="true" conf="prod" pattern="${web.src.dir}/WEB-INF/lib/[artifact].[ext]" />
    <ivy:retrieve sync="true" conf="dev" pattern="${lib.dir}/[artifact].[ext]" />
</target>

The clean-depends target to clean out the dependency directories.

1
2
3
4
5
6
<target name="clean-depends" description="Clean out the managed dependencies.">
    <delete>
        <fileset dir="${web.src.dir}/WEB-INF/lib" includes="*.jar" />
        <fileset dir="${lib.dir}" includes="*.jar" />
    </delete>
</target>

And finally, the depends-report target to generate a nice report of all the project dependencies.

1
2
3
<target name="depends-report" depends="depends" description="Generates dependency report for the project.">
    <ivy:report todir="${depends.report.dir}" conf="dev,prod" />
</target>

NOTE: At this point you will have to add these to the boilerplate file if you are using it – I will be creating a project for this build management stuff and sharing it out soon in a more official manner so stay tuned.

With these new targets you can refresh your dependencies and be ready to code with:

ant clean-all clean-depends depends test

Do this whenever you add/update/remove dependencies or when you pull the project out of source control… you don’t want to put your jars in source control any more if you are currently doing that. You can usually write rules/configurations in your source control to keep out the jar files. This keeps the storage space down and the transfer time down since you will have the jars stored in your local cache when you need them.

If you have never done automated dependency management you may not really see the value of it. You get the most benefit when you are working in a multi-project environment, which I will be supporting and blogging about soon.

Be warned that this post really only scratches the surface of what ivy can do. I recommend visiting their web site and checking out the documentation. They have decent documentation of all the config elements; however, their examples are a little on the weak side. You have to get into their sample code to get a real helpful guide.

You can find an updated version of this build script in my AntBoilerplate project.

Popularity: 1% [?]

  • Share/Bookmark

Tags: , , , ,

Layered Build Scripting

In a recent posting, Boilerplate Ant Build, I presented my basic Ant build starting point. This post takes that one step farther and presents suggestions for using an Ant build file as a tool box for building more “smart” build tools.

As a developer you should strive to keep your project buildable and accessible to all the bells and whistles of your IDE while also maintaining a simple straight-forward command line interface for use in other tools or at those times when you don’t need/want to fire up your IDE for a simple task. Ant is a great build automation tool that is supported on the command line and by all the major IDEs.

With your Ant Build Script you can compile, test, and deploy the project with simple command line entries or IDE actions. As I started to use this approach more and more I found that I ended up using the same commands over and over and some of them were on the long-ish side especially when typed in the command line:

ant clean-all test deploy-remote -Ddeploy.remote.conn=myuname:mypword@server:/home/myuname/artifacts

Not hard to pull from memory after you do it a few times, but when you are in a hurry you are more apt to make a mistake. What I like to do, and I recommend, is to create simple shortcut helper scripts for running these common commands. Your first thought might be to just add them to the build.xml file, and this is not a horrible idea, but I prefer keeping them separate from the “official” builder since in some cases these scripts may only work for my environment or under special circumstances. They are secondary tools that make use of your build tool kit.

So how should you write them? You could create a second Ant build file for your tasks and just call ant with that file, which could make ant calls into the main build
script.

ant -f yourfile.xml clean-dev-deploy

Nothing wrong with that, but I recommend learning a multi-platform scripting language such as Groovy or Ruby and doing your secondary script using that language. Apart from keeping your skills fresh and learning a new language, it also provides a very rich scripting environment that can be used on any platform supported by the language (Groovy and Ruby are supported on Windows, Linux and Mac). You may wonder why I am not promoting bash shell scripting here and one reason is that I have never been very fond of it, but also it is more tied to the operating system and less functional when compared with the other two I mentioned.

These scripts should reside in the root of the project directory right along side your build.xml file for ease of management and use. They should be checked into your source control, though you may want to XXX-out passwords (meaning you will need to checkout a copy and customize it for your use – and not check it back in) or parametrize it to add those in at run time.

The deployment command line I mentioned previously becomes simple and repeatable with a little Ruby foo…

1
puts `ant clean-all test deploy-remote -Ddeploy.remote.conn=myuname:mypword@server:/home/myuname/artifacts`

I end up with a simple script that will run the same set of tasks every time without a chance of me flubbing the directory or username. I tend to give them short concise names that distinguish what they are used for, such as devpush.rb in this case. You can point your IDE to this script and have it run it with the click of a button (at least in Eclipse you can, not sure of others). You can also run it on the command line

ruby devpush.rb

which is the most portable way, or if you make the script executable (chmod +x):

./devpush.rb

I have these scripts for every server that I deploy code to. Once you get the hang of your chosen scripting language you can also find a lot of other build/deployment related tasks to script such as the script I have for doing a tomcat deployment of a war file. It stops the server, archives the existing context, copies out the new war, cleans the log files and starts the server… all using a ruby script.

There are also some other interesting alternatives to Ant that I have taken an interest in lately, Gant and Gradle, both of which are build scripting DSLs based on Groovy; Gant is ant-like, while Gradle is more maven-esque. They both seem quite promising.

Keep your builds simple, fast and unambiguous so that you can focus on the real work of developing software of your project not for your project.

Side note on choosing between Groovy and Ruby

I love both Ruby and Groovy as scripting languages so it’s hard to really push one over the other. They each have their strengths and weaknesses. Ruby is great for build scripting and general command line task scripting. It has a rich library of built in functionality, it’s fast and you can get a lot of function for very little code. Groovy is best when you really need Java integration or Database integration, which is much simpler using JDBC… or when you need to do something more advanced that, as a Java developer you know how to do in Java, but not in Ruby. Groovy is just an extension of Java, though a very powerful one.

Popularity: 1% [?]

  • Share/Bookmark

Tags: , , ,

Boilerplate Ant Build

I have found it very useful to create a reusable boiler plate Ant build.xml file that I use as a starting point for all my projects. With a little bit of configuration property tweaking and perhaps a task modification here or there you have a standardized build environment which can be used by your IDE or on the command line.

I have provided a copy of my boilerplate build file at the bottom of this posting. It is provided for use under a creative commons license (Creative Commons Attribution 3.0 United States License), so please maintain the copyright header if you use or extend it.

If you look at the file you will see that the first section has all the properties you need. I used in-file properties rather than a separate properties file because I don’t really change them often for any given project and it keeps everything all in one neat little file. Technically you could get more re-usability by using properties files, but that assumes that you wont be modifying the build file itself for your project, which is something I do from time to time if something is not quite right for the project.

The first thing you will want to do is update the project name attribute to reflect the name of your project. Then you will want to update the configuration properties so that everything is correct for your build environment. I tried to keep everything pretty standard so that they don’t need to change much between projects.

The first property is the war.name which is the name you want the generated war file to have. I usually leave it set to the default, which is the project name.

1
<property name="war.name" value="${ant.project.name}" />

The next set of properties are the locations of your source directories, which default to the same values that I have set in my IDE.

1
2
3
<property name="src.dir" value="src" />
<property name="test.src.dir" value="test" />
<property name="web.src.dir" value="web" />

and then your external library directory.

1
<property name="lib.dir" value="lib" />

There has been some confusion around the external library directory property when I have shown this to others. This is not where all your libraries go, but where you put the libraries you don’t want in the war file. Things like JUnit and JMock jars go in there so that they are usable for testing and compiling but you don’t really want them to go into your deployed artifact. I the jars in this directory mapped on my classpath in my IDE too while those in the WEB-INF/lib are picked up by default.

The next group of properties define the artifact build locations:

1
2
3
4
5
<property name="build.dir" value="build" />
<property name="src.build.dir" value="${build.dir}/classes" />
<property name="test.build.dir" value="${build.dir}/test-classes" />
<property name="test.report.dir" value="${build.dir}/test-reports" />
<property name="webapp.build.dir" value="${build.dir}/webapp" />

As you can see, even if your build directory is called something else, like bin you can change everything else by updating that one property, build.dir.

The next property is the location of your local web server, where the war file would be deployed locally. The default is my symlink to the webapps directory for my local Tomcat server installation.

1
<property name="deploy.local.dir" value="/usr/local/tomcat/webapps" />

The final two properties are more environmental. You want to specifiy the JVM version you are targetting and whether or not you want debugging information to be compiled with your classes.

1
2
<property name="jvm.version" value="1.6" />
<property name="debug.enabled" value="true" />

You probably wont change these all that often, though it might be a good idea to disable debugging on production builds; I will have to look into supporting that. The jvm.version setting is nice because I think Ant still defaults to 1.3 or something like that.

The next section of the file contains all the task definitions. You will want to tweak these every now and then if you have special needs. You can run the Ant project help command (ant -p) to see the descriptions for all the tasks. Yes, I actually added descriptions for all of them.

Some common tasks I use a lot are:

ant clean-all test

Run all the tests on a clean build.

ant clean-all redeploy-local

Clean the build content and do a full local server redeployment.

ant clean-all test war

Run all the tests on a clean build and produce the war file.

I have used this in about five different projects now and it comes in really handy to have a standardized base point, especially when you are in a hurry and trying to do a quick command line build of the project; your commands are the same across your projects.

The follow-up article to this one will delve more into using the Ant build as a tool set and about adding scripting layers on top of the build to make repeated tasks bulletproof and quick.

Let me know if you have any suggestions for modifications or additions to this basic build script. It is a work in progress as I try to bring more simplicity into my development processes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<?xml version='1.0' ?>

<!--
   Ant Build Template (v1.0)
   Copyright (c) 2009 Christopher J. Stehno (chris@stehno.com)
   Creative Commons Attribution 3.0 United States License (http://creativecommons.org/licenses/by/3.0/us/)
   You are permitted to use this file in any personal and/or commercial product as long as you adhere to the above license.
-->

<project name="project-name" default="war">
   
    <!-- ============== -->
    <!-- Configurations -->
    <!-- ============== -->
   
    <property name="war.name" value="${ant.project.name}" />

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

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

    <property name="build.dir" value="build" />
    <property name="src.build.dir" value="${build.dir}/classes" />
    <property name="test.build.dir" value="${build.dir}/test-classes" />
    <property name="test.report.dir" value="${build.dir}/test-reports" />
    <property name="webapp.build.dir" value="${build.dir}/webapp" />

    <property name="deploy.local.dir" value="/usr/local/tomcat/webapps" />

    <property name="jvm.version" value="1.6" />
    <property name="debug.enabled" value="true" />

    <path id="classpath">
        <fileset dir="${web.src.dir}/WEB-INF/lib" includes="**/*.jar" />
        <fileset dir="${lib.dir}" includes="**/*.jar" />
    </path>

    <path id="test.classpath">

        <path refid="classpath" />
        <pathelement path="${src.build.dir}" />
    </path>

    <path id="test.runtime.classpath">
        <path refid="test.classpath" />
        <pathelement path="${test.build.dir}" />
    </path>

    <!-- ======= -->
    <!-- Targets -->
    <!-- ======= -->
   
    <target name="compile" description="Compiles the java sources.">
        <echo>Compiling java sources...</echo>
        <mkdir dir="${src.build.dir}" />
        <javac destdir="${src.build.dir}" classpathref="classpath" source="${jvm.version}" target="${jvm.version}" debug="${debug.enabled}">
            <src path="${src.dir}" />
            <include name="**/*.java" />
        </javac>

        <echo>Compiling java test sources...</echo>
        <mkdir dir="${test.build.dir}" />
        <javac destdir="${test.build.dir}" classpathref="test.classpath" source="${jvm.version}" target="${jvm.version}" debug="${debug.enabled}">
            <src path="${test.src.dir}" />
            <include name="**/*.java" />
        </javac>

        <echo>Copying over confg files...</echo>
        <copy todir="${src.build.dir}">
            <fileset dir="${src.dir}">
                <include name="**/*.xml" />
                <include name="**/*.properties" />
            </fileset>
        </copy>
    </target>

    <target name="clean-compile" description="Cleans up the compiled java sources.">
        <delete dir="${src.build.dir}" />
        <delete dir="${test.build.dir}" />
    </target>

    <target name="test" depends="compile" description="Runs all the tests (compiles if necessary).">
        <echo>Running unit tests...</echo>
        <mkdir dir="${test.report.dir}" />

        <junit printsummary="yes" haltonfailure="yes">
            <classpath refid="test.runtime.classpath" />
            <formatter type="plain" />
            <batchtest fork="yes" todir="${test.report.dir}">
                <fileset dir="${test.build.dir}" includes="**/*Test.class" />
            </batchtest>
        </junit>
    </target>

    <target name="clean-test" description="Cleans out all of the tests and test data.">
        <delete dir="${test.report.dir}" />
    </target>

    <target name="webapp" depends="compile" description="Builds the exploded web application (compiles if necessary).">
        <echo>Building web application...</echo>

        <mkdir dir="${webapp.build.dir}" />
        <copy todir="${webapp.build.dir}">
            <fileset dir="${web.src.dir}">
                <include name="**/*.*" />
            </fileset>
        </copy>

        <mkdir dir="${webapp.build.dir}/WEB-INF/classes" />
        <copy todir="${webapp.build.dir}/WEB-INF/classes">
            <fileset dir="${src.build.dir}">
                <include name="**/*.*" />
            </fileset>
        </copy>
    </target>

    <target name="clean-webapp" description="Cleans up the exploded web application.">
        <delete dir="${webapp.build.dir}" />
    </target>

    <target name="war" depends="webapp" description="Builds the war file (builds the webapp if necessary).">
        <echo>Building the war file...</echo>
        <jar destfile="${build.dir}/${war.name}.war">
            <fileset dir="${webapp.build.dir}" />
        </jar>
    </target>

    <target name="clean-war" description="Cleans up the war file.">
        <delete file="${build.dir}/${war.name}.war" />
    </target>

    <target name="clean-all" depends="clean-compile,clean-test,clean-webapp,clean-war" description="Cleans up all generated content." />

    <target name="deploy-local" depends="war" description="Deploys the war to the locally configured server location.">
        <echo>Deploying war to local server (${deploy.local.dir})...</echo>
        <copy todir="${deploy.local.dir}" file="${build.dir}/${war.name}.war">
        </copy>
    </target>

    <target name="undeploy-local" description="Removes the war (and exploded webapp) from the local server.">
        <delete file="${deploy.local.dir}/${war.name}.war" />
        <delete dir="${deploy.local.dir}/${war.name}" />
    </target>

    <target name="deploy-remote" depends="war,-prompt-for-server" description="Deploys the war file to the configured remote server (scp only).">
        <echo>Deploying war file to remote server via scp...</echo>
        <scp file="${build.dir}/${war.name}.war" todir="${deploy.remote.conn}" />
        <echo>You will need to finish the deployment from the server box.</echo>
    </target>

    <target name="-prompt-for-server" unless="deploy.remote.conn">
        <input message="Enter connection string (user:pass@server:path):" addproperty="deploy.remote.conn" defaultvalue="" />
    </target>

    <target name="archive" depends="clean-all" description="Cleans up and archives the project in a time-stampped zip file one directory up.">
        <echo>Creating archive of project...</echo>
        <tstamp />
        <zip destfile="../${ant.project.name}-${DSTAMP}-${TSTAMP}.zip" compress="yes" basedir="." />
    </target>

    <target name="sync-webapp" description="Syncs up the locally configured webapp content with the project.">
        <echo>Sync-ing the web code with local exploded war...</echo>
        <copy todir="${deploy.local.dir}/${war.name}">
            <fileset dir="${web.src.dir}">
                <exclude name="WEB-INF/**/*.*" />
            </fileset>
        </copy>
    </target>

    <target name="redeploy-local" depends="undeploy-local,deploy-local" description="Redeploys the local webapp (undeploy and deploy)." />
</project>

You can find an updated version of this build script in my AntBoilerplate project.

Popularity: 2% [?]

  • Share/Bookmark

Tags: , ,

Ant Input Prompting and Private Targets

I have found the Ant input tag useful lately for setting up runtime parameters of an Ant build. We have a few different server configuration settings that vary based on which server the artifact is being built for and the input tag makes this really easy:

1
<input message="Enter configuration name: " addproperty="config.name" defaultvalue="${config.name.default}" />

The downside of this is that it will prompt you to enter this every time you run the build, which can become annoying and really prohibits automated building. This is where the unless attribute of the target tag comes into play.

First create a private target (one whose name starts with “-”) that will prompt for the config name:

1
2
3
<target name="-prompt-for-config">
    <input message="Enter configuration name: " addproperty="config.name" defaultvalue="${config.name.default}" />
</target>

Then add the unless attribute to check for the presence of the config.name property:

1
2
3
<target name="-prompt-for-config" unless="config.name">
    <input message="Enter configuration name: " addproperty="config.name" defaultvalue="${config.name.default}" />
</target>

which will cause this task to be run only if the specified property is not set. The you can have other tasks depend on this private task, which will only run if you have not specified the config.name property on the ant command line.

1
2
3
<target name="compile" depends="-prompt-for-config" description="Compiles the java sources.">
    <!-- do stuff -->
</target>

Calling ant with the following will not prompt the user for the config.name:

ant compile -Dconfig.name=foo

I have used this in a few places now to make the build a bit more flexible, such as for doing server deployments, artifact installations, etc. It is a handy ant trick to keep in mind.

Popularity: 34% [?]

  • Share/Bookmark

Tags: , ,

Switch to our mobile site