| Tutorial: Using Maven from Ant |
|
|
|
| Written by Martin Senger | |
| Sunday, 17 February 2008 | |
Tutorial: Using Maven from AntMaven 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 preliminariesBefore you dive into technical chapters be aware that:
Quick summaryThis 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:
BasicsThe 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 repositoriesThe 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:
Defining dependenciesEach 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:
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 doIn 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.xmlYour 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.xmlThis 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 secondTherefore, this is how you build your project the first time: ant bootstrap antPlease 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.xmlIt 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"/> 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.pomThis 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 IDELet's start by an overview - there are three ways how to build (compiles, etc.) a project:
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 --> Antright-click in Ant View and Add Buildfiles --> select <your-project>/build.xml --> double-click bootstrap --> double-click initeclipseNote 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):
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 ) |


