Is there a way to generalize an Apache ANT target? - java

We have an Apache ANT script to build our application, then check in the resulting JAR file into version control (VSS in this case). However, now we have a change that requires us to build 2 JAR files for this project, then check both into VSS.
The current target that checks the original JAR file into VSS discovers the name of the JAR file through some property. Is there an easy way to "generalize" this target so that I can reuse it to check in a JAR file with any name? In a normal language this would obviously call for a function parameter but, to my knowledge, there really isn't an equivalent concept in ANT.

I would suggest to work with macros over subant/antcall because the main advantage I found with macros is that you're in complete control over the properties that are passed to the macro (especially if you want to add new properties).
You simply refactor your Ant script starting with your target:
<target name="vss.check">
<vssadd localpath="D:\build\build.00012.zip"
comment="Added by automatic build"/>
</target>
creating a macro (notice the copy/paste and replacement with the #{file}):
<macrodef name="private-vssadd">
<attribute name="file"/>
<sequential>
<vssadd localpath="#{file}"
comment="Added by automatic build"/>
</sequential>
</macrodef>
and invoke the macros with your files:
<target name="vss.check">
<private-vssadd file="D:\build\File1.zip"/>
<private-vssadd file="D:\build\File2.zip"/>
</target>
Refactoring, "the Ant way"

It is generally considered a bad idea to version control your binaries and I do not recommend doing so. But if you absolutely have to, you can use antcall combined with param to pass parameters and call a target.
<antcall target="reusable">
<param name="some.variable" value="var1"/>
</antcall>
<target name="reusable">
<!-- Do something with ${some.variable} -->
</target>
You can find more information about the antcall task here.

Take a look at Ant macros. They allow you to define reusable "routines" for Ant builds. You can find an example here (item 15).

Also check out the subant task, which lets you call the same target on multiple build files:
<project name="subant" default="subant1">
<property name="build.dir" value="subant.build"/>
<target name="subant1">
<subant target="">
<property name="build.dir" value="subant1.build"/>
<property name="not.overloaded" value="not.overloaded"/>
<fileset dir="." includes="*/build.xml"/>
</subant>
</target>
</project>

You can use Gant to script your build with groovy to do what you want or have a look at the groovy ant task.

Related

Migrate an ant script to Maven3

I'm migrating a maven 1 project to maven 3. The job is almost done actually with a missing task, What I need is to get all dependency names from pom file and write them to a configuration file as one string, the job is done like below in the maven.xml, check the last 5 lines where it writes the names to a file called wrapper.conf.
How can I achive this with Maven3? Is there a maven plugin I can use for this or I need to use ant script within my pom.xml?
<goal name="service">
<mkdir dir="${maven.build.dir}/grid" />
<ant:copy todir="${maven.build.dir}/grid">
<fileset dir="resources/javaservicewrapper" />
</ant:copy>
<j:forEach var="lib" items="${pom.artifacts}">
<j:set var="dep" value="${lib.dependency}"/>
<j:if test="${dep.getProperty('service.bundle')=='true'}">
<ant:copy failonerror="true" todir="${maven.build.dir}/grid/lib">
<fileset dir="${maven.repo.local}/${dep.groupId}/jars">
<include name="${dep.artifactId}-${dep.version}.${dep.type}"/>
<j:set var="SERVCP" value="../lib/${dep.artifactId}-${dep.version}.${dep.type}:${SERVCP}" />
</fileset>
</ant:copy>
</j:if>
</j:forEach>
<attainGoal name="jar" />
<ant:copy file="target/${maven.final.name}.jar" tofile="${maven.build.dir}/grid/lib/grid.jar" />
<j:set var="SERVCP" value="${SERVCP}../lib/gridcache.jar" />
<ant:copy todir="${maven.build.dir}/gridcache/conf" file="resources/javaservicewrapper/conf/wrapper.conf" overwrite="true">
<filterset begintoken="#" endtoken="#">
<filter token="service.classpath" value="${SERVCP}"/>
</filterset>
</ant:copy>
</goal>
EDIT : The solution using build-classpath worked well but I had other problems specific to using Javaservicewrapper. So best solution I found was creating whole the script/config file by appassembler-maven-plugin and let maven-assembly plugin to copy it to the conf folder
If you have the need to create a JSW (wrapper.conf) the simplest solution would be to use the appassembler-maven-plugin which can create such files.
Have a look at the build-classpath goal of the Maven Depency Plugin. You can fast check the result on the command line:
mvn dependency:build-classpath
You can change the path to the dependeny files using the 'prefix' (mdep.prefix) property
mvn -Dmdep.prefix=myLibFolder dependency:build-classpath
You will find much more configuration parameters in the documentation, e.g. the outputFile parameter ;-)

How to add extraClasses option to Ant script when calling java2wsdl?

I'd like to pass extraClasses parameter when I generate java2wsdl. Here is my Ant task:
<target name="rews.all" depends="xews.aar">
<echo message="${axis2.classpath}" />
<delete file="${build.dir}/wsdl/XEWS.wsdl" />
<taskdef name="java2wsdl"
classname="org.apache.ws.java2wsdl.Java2WSDLTask"
classpathref="axis2.classpath">
</taskdef>
<java2wsdl className="com.dd.xews.XEWS"
outputLocation="${build.dir}/wsdl/"
targetNamespace="http://xews.dd.com/"
schemaTargetNamespace="http://xews.dd.com">
<classpath>
<pathelement path="${axis2.classpath}"/>
<pathelement location="${build.dir}/classes"/>
<pathelement location="${vendor.dir}/AWS/lib/aws-java-sdk-1.2.1.jar"/>
</classpath>
</java2wsdl>
<copy todir="${build.dir}/" file="${build.dir}/wsdl/XEWS.wsdl"/>
</target>
Tried everything but no luck.
Does anyone know the syntax? How do I add extraClasses here?
Test1 (failed)
This failed with error java2wsdl doesn't support the "extraClasses" attribute:
<java2wsdl className ="com.dd.xews.XEWS"
outputLocation ="${build.dir}/wsdl/"
targetNamespace ="http://xews.dd.com/"
schemaTargetNamespace ="http://xews.dd.com"
extraClasses ="com.dd.xews.XEWS.Emailer.java">
</java2wsdl>
How to find out which attributes java2wsdl Ant task does support?
My Axis2 version is 1.5.4.
Here's a link to the source code of Ant task: Java2WSDLTask.
#setExtraClasses accepts String parameter, and then tries to split it using comma delimiter. So try passing something like
<extraClasses>com.test.Class1,com.test.Class2</extraClasses>
EDIT
This will not work in older versions of Axis2 (to be more precise -- versions prior 1.6.0). It's because of 'extraClasses' attribute was specified as an array-type, which is obviously not supported as Ant task attribute. You can find all the details in this JIRA issue: AXIS2-4634: Ant task Java2WSDLTask does not allow the use of extraClasses
The easiest way to make it work is to upgrade Axis2 JARs to a newer 1.6.x version. If you are stuck with Axis2 version for some project-specific reasons (I don't see there should be any), you could take the source code of Java2WSDLTask from the newer version (see link to GrepCode above), and make a copy of this task in your project (you'll have to use different class name, or package), then use it as an Ant task just like you are using it currently. Except for it will be possible to use 'extraClasses' attribute.
Axis2 1.4 and up support the "-xc" attribute. Here's how I did it:
<java classname="org.apache.ws.java2wsdl.Java2WSDL" fork="true">
....
<arg value="-xc"/>
<arg value="com.example.mypackage.MyClass"/>
<arg value="-xc"/>
<arg value="com.example.anotherpackage.MyOtherClass"/>
....
</java>

How to validate properties/custom configuration files?

My application has many properties/text configuration files. Is there a common way to validate these kind of files? May be some tools or something like xsd?
The files are large (more 1000 rows), so I frequently make mistake. Information in file in majority of cases - is path to different directories. So it will be good if they exist and are consistent. E.g.
if I have
mydata.root="c:\data"
and after I have:
myreports=${mydata.root}/reports
that will be good check that c:\data and c:\data\report exist
and not written (some hundreds rows down) e.g.
myreports=${mdata.root}/reports
You could do this validation in your build file.
For example, the following build file defines a macrodef validate-file-property which validates that a specified property is defined and that it exists as a file or dir in the file system.
<project default="init">
<property file="test.properties"/>
<target name="init">
<validate-file-property property="program.files" type="dir"/>
<validate-file-property property="mydata.root" type="dir"/>
<validate-file-property property="foo"/>
</target>
<macrodef name="validate-file-property">
<attribute name="property"/>
<attribute name="type" default="file"/>
<sequential>
<fail unless="#{property}" message="The property '#{property}' is undefinded."/>
<available file="${#{property}}" property="#{property}.exists" type="#{type}"/>
<fail unless="#{property}.exists" message="The #{type} '#{property}' with value '${#{property}}' does not exist."/>
</sequential>
</macrodef>
</project>
You need to decide when to validate the properties, and you need to validate them explicitly - as shown in the init target in this example.
BTW, if you used a standardized pattern to name properties which refer to files or directories - e.g. my.special.file, my.build.dir - then you could use a shell script to discover all relevant properties and write all your validate-file-property elements . Something like this:
awk -F= '/\.(file|dir)/{ printf "<validate-file-property property=\"%s\" type=\"%s\"/>\n", $1, gensub(/.*\.(file|dir)/, "\\1", "g", $1) }' *.properties
You could paste the output into your build file.
I once offered a bounty for information on configuration languages other than XML that provide schema validation. Unfortunately, such configuration languages are few and far between.
Here is a link to the bountied question, in case you want to see the sparseness of the responses.

Filtering sources before compilation in NetBeans & Ant

I need to filter java files before compilation, leaving the original sources unchanged and compiling from filtered ones (basically, I need to set build date and such).
I'm using NetBeans with its great Ant build-files.
So, one day I discovered the need to pre-process my source files before compilation, and ran into a big problem. No, I did not run to SO at once, I did some research, but failed. So, here comes my sad story...
I found the "filter" option of "copy" task, overrided macrodef "j2seproject3:javac" in build-impl.xml file and added filter in the middle of it. I got the desired result, yes, but now my tests are not working, since they use that macrodef too.
Next, I tired to overriding "-do-compile" target, copying&filtering files to directory build/temp-src, and passing an argument of new source directory to "j2seproject3:javac":
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,
-compile-depend,-prepare-sources"
if="have.sources" name="-do-compile">
<j2seproject3:javac gensrcdir="${build.generated.sources.dir}" srcdir="build/temp-src"/>
<copy todir="${build.classes.dir}">
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
</copy>
</target>
And now Ant says to me, that macrodef in question does not exist!
The prefix "j2seproject3" for element "j2seproject3:javac" is not bound.
That's strange, since build-impl.xml contains that macrodef, and build-impl.xml is imported into main build file.
And, by the way, I cannot edit build-impl.xml directly, since NetBeans rewrites it on every other build.
So, my question is: how can I automatically filter sources before compiling in NetBeans, and do not break the build process?
Looking at the default build.xml, it contains a comment that reads (in part):
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
So, to inject some pre-compile processing, you would provide a definition for -pre-compile.
FWIW, the error you got is because the j2seprojectX prefix is defined on the project tag of build-impl.xml, and the code in build.xml is outside of that tag.
Since I found an answer, and because it seems that nobody knows the answer, I'll post my solution.
build.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Needed to add xmlns:j2seproject3 attribute, to be able to reference build-impl.xml macrodefs -->
<project name="Parrot" default="default" basedir="." xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3">
<import file="nbproject/build-impl.xml"/>
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml, -compile-depend,-prepare-sources" if="have.sources" name="-do-compile">
<j2seproject3:javac gensrcdir="${build.generated.sources.dir}" srcdir="build/temp-src"/>
<copy todir="${build.classes.dir}">
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
</copy>
</target>
<!-- target to alter sources before compilation, you can add any preprocessing actions here -->
<target name="-filter-sources" description="Filters sources to temp-src, setting the build date">
<delete dir="build/temp-src" />
<mkdir dir="build/temp-src" />
<tstamp>
<format property="build.time" pattern="yyyy-MM-dd HH:mm:ss"/>
</tstamp>
<filter token="build-time" value="${build.time}" />
<copy todir="build/temp-src" filtering="true">
<fileset dir="src">
<filename name="**/*.java" />
</fileset>
</copy>
</target>
</project>

How to automate the update of dozens of mercurial repositories with ant?

I have a legacy product, spread across dozens of repositories. I'm currently trying to refactor (and understand...) the given build process. First step was moving from the old version control system to mercurial, which was encouraging and easy.
The build process mainly uses ant build scripts (good news) but has to run 'on' the repository file structure (bad news...) because the ant scripts take files from all repositories and leave artifacts on .. lets say, several places... But that's not a blocker.
Before I trigger the build (now with hudson CI), I have to make sure, that all repositories are updated to the tip or a selected tag. I could write a script/batch or write a custom ant task (again) but, I don't want to ignore existing features (again):
is it possible to update a set of mercurial repositories by using existing ant tasks or mercurial features? All repositories are located in one folder (like /repo) and have a common prefix (like SYSTEMNAME_module) so it's easy to locate them/create a fileset.
The most simple solution today is to start the hg executable from Ant (using the exec task. Note: MacroDef is your friend). Just create a "main" build file in one of the projects, change to the parent directory (where you can access all the local copies) and then update each one.
I found a different ant based solution, based on the 'apply' task:
<apply executable="hg">
<arg value="update" />
<arg value="--verbose" />
<arg value="--revision" />
<dirset dir="/repos" include="REPO_SUFFIX*" />
</apply>
Thanks to Aaron, his very helpful answer showed me the right direction.
I think this would be a more flexible way to update a mercurial depo. From a set of directories it chooses which ones are depots and updates those. Note that you can also do this with the foreach tag of ant contrib but the 'for' seems to consume a lot less memory.
<!-- See if the directory looks like a mercurial depo, if so update it -->
<target name="-mercurial-update">
<dirset dir="${dir.deps}" id="lib.dirs">
<exclude name="build"/>
<present targetdir="${dir.deps}">
<mapper type="glob" from="*" to="*/.hg" />
</present>
</dirset>
<for param="file">
<path>
<dirset refid="lib.dirs"/>
</path>
<sequential>
<echo>Trying to update mercuriali depo #{file}</echo>
<exec executable="hg" dir="#{file}">
<arg line="pull" />
</exec>
<exec executable="hg" dir="#{file}">
<arg line="up" />
</exec>
</sequential>
</for>
</target>

Categories