Home arrow Pantheon Tutorials arrow Tutorial: Using Maven from Ant
 
   
 
Tutorial: Using Maven from Ant PDF Print E-mail
Written by Martin Senger   
Sunday, 17 February 2008

Tutorial: Using Maven from Ant

Maven is a software project management. It has many nice features - sometimes it is considered as a next generation of Apache Ant - but it takes also some time to get used to it. Fortunately, the best features of Maven - the dependency management and central repositories of the 3rd-party Java libraries - can be used also from Ant, after changing very little in the existing Ant's build.xml configuration files.

This tutorial is about it: what is needed to do in order to use Maven from your Ant.

 

Table of Contents

 

Moral preliminaries

Before you dive into technical chapters be aware that:
  • All GCP projects, except those using Eclipse RCP, are required to define their own Ant configuration file (build.xml). Which means that the projects can be built (and used) also outside Eclipse IDE (that most GCP developers use).

     

  • All GCP projects, again except those using Eclipse RCP, are required (starting from March 2007) to define their dependencies on other projects and on the 3rd-parties libraries using the Maven dependencies files.

 

Quick summary

This tutorial is doing roughly the same thing as the documentation of the Ant tasks for Maven. You may find preferable to go directly there.

There are also two GCP projects that can serve as examples how to change the Ant's build.xml file, and how to define your dependencies. It is recommended to check them out and use them as templates for your own projects:

 

Basics

The main feature of Maven is that it knows how to access remote repositories with Java libraries. You do not need to download all necessary .jar files yourself, you just define what libraries your project needs. Maven does the rest. Which means that the 3rd-party libraries do not fill anymore your (usually) lib directory.

 

Maven repositories

The places where Maven finds wished libraries are regular directories served by a normal Web HTTP server (no new daemons or specialised servers are needed), or sitting in a local file-system.

Also the traffic is low because Maven makes automatically local copies (cache) on the users machines so the download happens only the first time (or when a library in a repository changed).

The repositories are roughly of three types:

  1. A remote repository with all general Java libraries (such as commons-logging). You do not need to specify its location anywhere because it will be used by default if the next two repository levels do not contain wanted files. You can browse this default repository here.

     

  2. A GCP-specific repository for code that is created and shared by GCP developers. Its URL is http://maven.generationcp.org/m2repo/ (which is an alias to http://cropwiki.irri.org/m2repo/).

     

  3. A local repository on user machines (local caches). It is created automatically when the user starts to use Maven. Its default location depends on the operating system used:

     

    • On Linux it is created in the .m2 directory in the user's home directory.

       

    • On Windows, the default location is C:Documents and Settings${username}.m2.

    The local repository stores jar files on one place - and all your projects can reuse them. Later you will see how to make reference to these shared jar files in your Ant build.xml file.

    In some instances (i.e. disk space considerations), you may wish to host the local maven repository somewhere else. To achieve this, you need to create a settings.xml file and put it the .m2 directory (whose location again differs on Linux and Windows, see above). The file has the following contents:

    							<settings>
    	<localRepository>yourLocalRepositoryPath</localRepository>
    	</settings>
    	
    Other settings can be found on the maven site. If you do not have any settings you may need to add an empty settings.xml file to avoid build errors. The file should contain at least the following
    							<settings/>
    	

     

 

Defining dependencies

Each project (for a more complex project, each part of a project) has its identification, and a list of dependencies. Both, identification and each dependency, have at minimum three required attributes:
  • groupId identifies a group where your project (or its part) belongs to,
  • artifactId identifies your project (or its part) within the group, and
  • version defines its version. A special version, indicated by letters SNAPSHOT at its end, means "the latest version".

    In the GCP projects, please use version with three parts: K.L.M where K is a major version number, L is a minor version number, and M is a bug fix version number. Which can be followed by -SNAPSHOT.

For dependencies, these three attributes should be enough to identify them word-wide uniquely. Therefore, in the groupId, use the similar rules as in the Java package names (e.g. org.generationcp.base).

For the project identification use also description, human-readable name and identify there also at least the main developer of the project.

An example of such identification file is in project.pom.

The Maven dependency management is a complex and strong tool. It includes transitive dependencies (dependencies are chained), scope recognition and SNAPSHOT handling. See more in the Introduction to the Dependency Mechanism.

 

What you need to do

In order to "mavenize" your Ant, you need to change slightly your Ant's build.xml file, and to create few other XML files (recommended place for them is a sub-directory xmls):
build.xml
Your build.xml should start (bold font indicates the Maven-related changes):
<?xml version="1.0"?>
<!DOCTYPE project [
<!ENTITY maven        SYSTEM "xmls/maven.xml">
<!ENTITY upload2maven SYSTEM "xmls/upload2maven.xml">
]>
<!-- =================== Build for PROJECT =================== -->
<project name="Validation tools for GCP Data Sources"
default="compile" basedir="."
xmlns:artifact="urn:maven-artifact-ant"> 
Later (anywhere after defining that properties can be also read from the build.properties file), you include your other XML files (but not yet the dependencies file(s)):
&maven;
&upload2maven; 
Maven needs some initialization. It is done by a task initmaven (the task itself is in the included file xmls/maven.xml). Therefore, make the usual Ant's init task dependent on the initmaven:
<target name="init" depends="initmaven">
Last but definitely not least, your Ant's tasks need to know where are files with the 3rd-party libraries (those you defined as dependencies). Definitely the compilation task needs to know, and very probably you will have some tasks that need the CLASSPATH as string. For this purposes, Maven sets two properties (you can see them being defined and set in xmls/maven.xml) - dependency.classpath and dependency.fileset.

Use the dependency.classpath in your compilation and javadoc tasks:

<javac srcdir="${src.main}"
destdir="${build.classes}">
<classpath refid="build.classpath"/>
<classpath refid="dependency.classpath"/>
</javac>
...
<javadoc
...
use="true"
public="true">
<classpath refid="build.classpath"/>
<classpath refid="dependency.classpath"/>
<sourcepath>
<pathelement location="${src.main}"/>
</sourcepath>
...
</javadoc>
and use the dependency.fileset whenever you need the list of dependencies as a string. For example:
<pathconvert property="libs.path" refid="dependency.fileset"/>
<copy todir="${build.run}">
<fileset dir="${src.etc}" includes="run-* source.me* cp.bat"/>
<filterset>
<filter token="PROJECT_HOME" value="${basedir}"/>
<filter token="LOG4J"        value="${log4j.value}"/>
<filter token="PROJECT_DEPS" value="${libs.path}"/>
</filterset>
</copy>
If you wish to deploy your project to a Maven repository, you will need also a task making a project jar file. Such task is usually there anyway, disregarding Maven. You will need to use this task's name in the xmls/upload2maven.xml file - the best is to keep the default name jar. See in the example below the recommended way how to name the resulting project jar file - it uses the artifact name defined in the xmls/project.com file - where you have characterized your project:
<target name="jar" depends="compile"
description="Create a jar file with project classes.">
...
<property name="project.jar" value="${maven.project.artifactId}.jar"/>
...
And that is all what needs to changed in the build.xml. `
xmls/maven.xml
This file contains general Maven-related stuff which usually stays unchanged for all projects (you can just copy it from other projects).

You need to edit the initmaven task to reflect the number and names of your dependencies files. If you have only one such file, and if you name it project.pom, you do not need to change anything:

<artifact:pom id="maven.project" file="xmls/project.pom"/>
The Ant needs a Maven library in order to understand Maven dependencies etc. This library is defined here as well:
<property name="maven.ant.file" value="maven-ant-tasks-2.0.7.jar"/>
The library cannot be fetched from any Maven repository because only when we have it we can use Maven. This Catch-22 situation is solved by a special task bootstrap that must be called the first time you are building your project. If you forget it you will see some help:
ant
Buildfile: build.xml
checkmaven:
BUILD FAILED
/home/senger/ds-tools/xmls/maven.xml:58: Sorry...
Missing Maven library. It can be fetched from the Internet.
Type: ant bootstrap
This is needed only the first time.
Total time: 1 second
Therefore, this is how you build your project the first time:
ant bootstrap
ant
Please be aware that the bootstrap task is only one possible solution. It is not part of the Maven itself but it is a recommended way for the GCP developers (an alternative would be to include this library into the lib directory manually).

 

xmls/upload2maven.xml
It contains tasks that deploy (upload) project specific jar files to a Maven repository. Therefore, it is optional (because not all projects will end up in a Maven repository). But it is recommended to keep it in all projects anyway.

In Maven terminology, the deploying means to upload a jar file to a remote Maven repository, and the installing means to copy a jar file to a local Maven repository (which is still on your machine). Correspondingly, this file defines two tasks:

<target name="mdeploy" depends="initmaven,jar"
description="Deploy all parts of this project to a remote Maven repository">
...
</target>
<target name="minstall" depends="initmaven,jar"
description="Install all parts of this project in a local Maven repository">
...
</target>
You need to edit this file only if your Ant's task for creating project jar file has a different name than jar.

There may be more such tasks if your project produces more jar files.

If you are a developer that has a write-access to the remote GCP Maven repository, you usually need to specify some properties with your name and with your private key. The best way is to put them into build.properties file:

# --- what is your user name on the host where is GCP Maven repository
# Default value (usually not fine): ${user.home}
maven.auth.username = ...
# --- where is your private key on your local machine
# Default value (often fine): ${user.home}/.ssh/id_rsa
maven.auth.privkey = ...
In order to deploy to the GCP Maven repository you need to copy there your public key. You can do it (on Unix) using this command (change there the name of the file with your public key, and the user name):
cat ~/.ssh/id_rsa.pub | ssh -l msenger cropwiki.irri.org 'cat - >> ~/.ssh/authorized_keys'
Of course, you can do it only after you create a directory .ssh on the host with the GCP Maven repository (by logging there normally by ssh).

There is an 'mnotify' ant target in mdeploy2maven.xml that allows the sending of notification email whenever someone deploys a new version of a software component to the Maven repository. Here is what a developer needs to do in order to notify others about her/his deployment:

Things you need to do only once (disregarding how many projects you are going to deploy)

1.  Your Ant needs additional libraries. I do not want to put them into  project dependencies because they are used really only for   deployment. Therefore, you need to download them and install on your   machine. They are:

  mailapi.jar
  smtp.jar
  activation.jar (not needed if you are using Java 6)

  You may even have them already in your local Maven repository (in  the javax directory). Otherwise, the first two are part of the  JavaMail package, downloadable from  http://java.sun.com/products/javamail/downloads/index.html, the  latter is from JavaBean Activation Framework, downloadable from  http://java.sun.com/javase/technologies/desktop/javabeans/jaf/index.jsp.

  Once you have them, you can put them where your Ant can find  them. Which is one of the following places:

    Put it in your ${ANT_HOME}/lib directory.
    Or, put in your ${HOME}/.ant/lib directory.

2.  You need to specify some properties (actually usually just one)  about your SMTP server (a server that is used to get the outgoing
  email from your computer to the world).

  Background information: In order to send an email, your computer  (the software sending the email) must be able to contact an SMTP  server. Some SMTP servers do not need authentication (i.e. your user  name and password), some do. For example, the HERMES SMTP server  that runs in the IRRI internal network does not need it, but  (actually, therefore) it can send emails only to the cgiar.org  addresses. Which is not our case because our notification is going  to be sent to a new cropforge mailing list (more about it below).

  Because we are going to use an SMTP server that needs to know your  user name and your password, we have a small problem here. The Ant  needs your password in a property. One way to set a property is to  define it in your (or project's) build.properties file. But it is  there as a plain text - which is not good for passwords. Another way  is to let Ant to ask you to type it - but Ant (actually Java behind
  it) cannot avoid to echo your input on the screen - and (unless we  write a special piece of handler) it shows again your password to  all watching eyes in plain text.

Therefore, here is a compromise: a new gmail account was created  ( This e-mail address is being protected from spam bots, you need JavaScript enabled to view it ) which we are going to use to send the  notification emails from, and whose password is not that crucial as  your own personal passwords, and whose password we will share. The password was sent to all developers in a  separate email. If you are a new developer  or you did not receive an email about the password, pleae email Martin Senger). [Remember that I am still talking only about  developers who deploy to Maven.]

Once you get the password, put it (either permanently, or just for  the time of deployment) into your build.properties file as a  property "smtp.password". This property also decides whether to send  notification, at all: if this property does not exist, the Ant will  not even try to send the notification email.

Things you need to do in all your projects that you are going to deploy - but again, only once for each project

* Edit xmls/upload2maven.xml. Text in bold are new lines to be  added (I am going to make this change also to the "empty project"   package that is used for new projects):

<target name="mdeploy" depends="initmaven,jars"
  description="Deploy all parts of this project to a remote Maven repository">

  <artifact:install-provider artifactId="${maven.install.provider}" version="1.0-beta-2"/>

  <artifact:deploy file="${build.lib}/${maven.project.artifactId}-${maven.project.version}.jar">
    <remoteRepository url="${maven.repository.scp}">
      <authentication username="${maven.auth.username}" privateKey="${maven.auth.privkey}"/>
    </remoteRepository>
    <pom refid="maven.project"/>
  </artifact:deploy>

  <antcall target="mnotify" />

</target>

<target name="mnotify" depends="init,initmaven" if="smtp.password">

  <property name="notification.target"  value=" This e-mail address is being protected from spam bots, you need JavaScript enabled to view it "/>
  <property name="source.email.address" value=" This e-mail address is being protected from spam bots, you need JavaScript enabled to view it "/>
  <property name="smtp.user"            value=" This e-mail address is being protected from spam bots, you need JavaScript enabled to view it "/>
  <property name="smtp.host"            value="smtp.gmail.com"/>
  <property name="smtp.port"            value="465"/>
  <property name="smtp.tls"             value="on"/>

  <mail
    from="${source.email.address}"
    tolist="${notification.target}"
    mailhost="${smtp.host}"
    mailport="${smtp.port}"
    user="${smtp.user}"
    password="${smtp.password}"
    ssl="${smtp.tls}"
    subject="Deployed: ${maven.project.artifactId}-${maven.project.version}">
    <message>
A new release was deployed to Maven repository ${maven.repository}.

Released by: ${maven.auth.username}
Released at: ${TODAY_LONG}

GroupId: ${maven.project.groupId}
ArtifactId: ${maven.project.artifactId}
Version: ${maven.project.version}

Name: ${maven.project.name}
Description: ${maven.project.description}
    </message>
  </mail>
</target>


Things to do by those who are interested to get notification about any deployment to Maven

* Subscribe to the new pantheon-releases mailing list on CropForge:
  http://lists.cropforge.org/mailman/listinfo/pantheon-releases.


xmls/project.pom
This file defines identity of your project and its dependencies. For each jar file produced by a project there will be one such file.

Using Maven dependencies in Eclipse IDE

Let's start by an overview - there are three ways how to build (compiles, etc.) a project:
Using Ant
Instructions what and how to build are distributed together with every project in a file build.xml (yes, you hear it correctly, with every project, and if your project does not have an Ant's build.xml file yet, check the GCP Best Practices please - and add it to your project). You do not need to change anything in this file, the file contents is shared by CVS/SVN (and if you need something personal, local, put it in the file build.properties which is not shared by CVS/SVN).

 

Using Maven
In the GCP development, we are not using Maven directly but we are using the Ant's ability to call Maven and use its nice features (dependencies management, remote repositories, etc.). Well, you just read a tutorial about it...

 

Using Eclipse
Eclipse IDE has a mechanism that compiles (or builds in eclipse terminology) code found in one or more source code directories which are on the eclipse build path. When the Eclipse builds it will highlight code errors and warnings in the appropriate code editors and views. The dependencies for this code are defined in the .classplath file in the project root directory. This .classplath file is not edited directly, but via the 'Java Build Path' section in the project properties (which can be found in the File Menu).

 

Having three ways (which you have if you are an Eclipse user), you face a typical problem "Which one to use?".

The solution is to install and use the Eclipse Plugin for Maven 2. Because only Maven can read the dependencies defined in the xmls/project.pom file.

Therefore, download and install the Eclipse Plugin for Maven 2. Better still, use the Eclipse Software Update Manager using the URL http://m2eclipse.sonatype.org/update. For information on how to the use Update Manager to install Eclipse plug-ins please see the How to use the Update Manager. Restart Eclipse IDE.

Once you restarted your Eclipse, do not yet enable Maven (and if you have already done it, just disable it again for a moment). It is because Maven needs the dependencies file in a different place than we have it. Maven expects this file to be pom.xml in the project root directory, and we have the xmls/project.xml file.

The Ant comes to help: Call the Ant's target initeclipse. Either from the command line, but better (you are after all an Eclipse user, aren't you?) open an Ant View

Window --> Show View --> Ant
right-click in Ant View and
Add Buildfiles --> select <your-project>/build.xml
--> double-click bootstrap --> double-click initeclipse
Note that he bootstrap task is to be called only once, at the very beginning.

Now Refresh! - F5 and go and enable Maven:

right-click on your project name --> Maven2  --> Enable
[ If a Maven wizard appears on the screen, you have not refreshed your workspace, or something else went wrong. Simply close the wizard and start to investigate. But do not let the Maven wizard to take over. ]

The initeclipse creates several pom.xml files on the right places. Enabling Maven reads these files and changes Eclipse's 'Java Build Path'. It does a reasonable job - but unfortunately not a complete job. There are still two things you have to do manually in the 'Java Build Path' (until we find how to convince Maven or Ant to do it for you):

  • By default, Eclipse creates classes in a different directory (bin or target). Which does not matter much now (Eclipse compiles, no errors, you can start debugging etc.) but you will not be able to use scripts that may be part of the project and that are generated by Ant (and therefore they expect classes in a different place). Better to change it:
    							right-click on your project name --> Build Path -->
    	Configure Build Path --> Source
    	
    Check Allow output folders for sources folders and make Output folder pointing to build/classes.

     

  • If your project uses some additional libraries (jar files) that are not defined in the dependencies file, Maven does not know about them and therefore does not put them into 'Java Build Path'. But usually it does not matter because when you created a new Java project, Eclipse was clever enough and put it there already (these libraries are in the lib directory). So no work for you...

    Well, unless or until you go to the Maven2 menu and click on "Update Source Folders". That is a signal for Maven to put away everything it does not know about. The recommendation is obvious: never click on this menu item.

     

 


This e-mail address is being protected from spam bots, you need JavaScript enabled to view it
This e-mail address is being protected from spam bots, you need JavaScript enabled to view it
This e-mail address is being protected from spam bots, you need JavaScript enabled to view it
Last Updated ( Wednesday, 25 February 2009 )