Ant build script to create EAR with multiple WARs - ant

I want to create Ant build script to build EAR from multiple WARs. I have 3 different web projects(3 different WARs). I need to know where I should keep the build.xml which builds EAR.
Should I create a new EAR project in the parent folder of those 3 projects?

Have you considered looking at Gradle. Gradle has support for multi-module projects
and if you follow their conventions, the build scripts are fairly straight-forward. For this project, you would have three projects that generate a war (from each) and then a parent project that creates the ear and pulls in the war artifacts to create the ear file.
Worth a look, but I know you asked about Ant.

I have parent project with build.xml and subfolders containing individual modules
build.xml
ear-module/
ejb-module/
web-module/
The build.xml calls ant for each module and then assembles them together to result ear.
This is sample from single-ant having multiple modules. I prefer to have individual ant files in each module that is called from master folder. Dependencies can be tricky in ant. This is probably the reason why people favour maven/graddle over ant.
<target name="war" depends="ejb">
<mkdir dir="${build.dir}/war" />
<path id="web-classpath">
<path refid="classpath" />
<file file="${build.dir}/${ant.project.name}.jar" />
</path>
<javac srcdir="web-module/java" classpathref="web-classpath" destdir="${build.dir}/war" debug="true"
source="${compiler.source.level}" target="${compiler.target.level}" encoding="${src.encoding}"/>
<war destfile="${build.dir}/${ant.project.name}.war" webxml="web-module/war/WEB-INF/web.xml">
<webinf dir="web-module/war/WEB-INF">
<exclude name="web.xml"/>
<exclude name="classes/**"/>
<exclude name="lib/**"/>
</webinf>
<classes dir="${build.dir}/war" />
<lib dir="web-module/war/WEB-INF/lib"/>
<fileset dir="web-module/war">
<exclude name="WEB-INF/*"/>
<exclude name="META-INF/*"/>
</fileset>
</war>
</target>

Related

Copy entire directory before creating an ear file

I am trying to build an ear file from an ant script. There are several targets that create .jar and .war (to be contained within the ear) files using different projects and these are building without issue so I will not include them.
Imagine this directory structure:-
Project1/
build/
lib/
META-INF/
build.xml
So when the ant script is called the build directory is deleted and remade, all fairly standard stuff. Then I create the jar's and war's from external projects and store them in build/ - everything is fine.
But I also want to include the directories lib/ and META-INF/ in the ear file. So I try to copy them to the build directory using this target.
<target name="file_cleanup">
<copy todir="${build}">
<fileset dir="lib/"/>
<fileset dir="META-INF/"/>
</copy>
</target>
This file_cleanup target is a dependant of the default build target which creates the ear - shown below:
<target name="ear" depends="initialise, file_cleanup, other targets...">
<ear destfile="My.ear" appxml="META-INF/application.xml">
<fileset dir="${build}" includes="*.jar,*.war"/>
</ear>
</target>
What I want to see when I extract the ear is:
target1.jar
target2.war
lib/
META-INF/
But what I actually get is:
target1.jar
target2.war
and all of the contents of both the lib and META-INF directories...
I was able to resolve this issue by creating additional properties and directories and copying the directory structures to the new directories:
<target name="initialise">
<delete dir="${build}"/>
<mkdir dir="${build}"/>
<mkdir dir="${build}/${lib}"/>
<mkdir dir="${build}/${meta-inf}"/>
</target>
<target name="file_cleanup">
<copy todir="${build}/${lib}">
<fileset dir="lib"/>
</copy>
<copy todir="${build}/${meta-inf}">
<fileset dir="META-INF"/>
</copy>
</target>

Exclude empty directories with Jar Jar Links

I am using an Ant task from Jar Jar Links to embed classes from a 3rd-party jar file (objenesis) in my distributable jar file (example.jar). Jar Jar will translate classes from the original package (org.objenesis) to one of my choosing.
It works but it leaves empty directories in the distributable jar.
Here is a simplified build.xml:
<target name="jar" depends="compile">
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask"
classpath="lib/jarjar-1.1.jar"/>
<jarjar jarfile="dist/example.jar" excludes="org.objenesis.**">
<fileset dir="build/classes/main"/>
<zipfileset src="lib/objenesis-1.2.jar"/>
<rule pattern="org.objenesis.**" result="org.thirdparty.#1"/>
</jarjar>
</target>
A sample of contents of the example.jar includes (as expected):
org/thirdparty/Objenesis.class
org/thirdparty/ObjenesisBase.class
but also these empty directories (undesirable):
org/objenesis/
org/objenesis/instantiator/
org/objenesis/instantiator/basic/
My question: how to I exclude these empty directories?
I tried the "zap" option (listed in the doc), but that didn't work.
This appears to be a known issue in Jar Jar, listed in their issue tracker: http://code.google.com/p/jarjar/issues/detail?q=empty&id=32
Given that this was raised almost three years ago and doesn't appear to have got any traction, I suppose your options are to contribute a fix, or to work around it.
An example Ant target to work around it, taking advantage of Ant's support for removing empty directories on copy, would be:
<target name="unpolluted-jarjar" description="JarJars without empty directories">
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpath="${location.lib}/build/jarjar-1.2.jar"/>
<jarjar basedir="${location.classes}" destfile="${location.dist.binaries}/my-app.jar">
<zipfileset src="${location.lib}/shipped/dependency.jar"/>
<rule pattern="com.example.dependency.**" result="com.example.my-app.jarjar.com.example.dependency.#1"/>
</jarjar>
<mkdir dir="${location.dist.binaries}/exploded"/>
<unzip src="${location.dist.binaries}/my-app.jar" dest="${location.dist.binaries}/exploded/my-app.jar"/>
<copy includeemptydirs="false" todir="${location.dist.binaries}/unpolluted/my-app.jar">
<fileset dir="${location.dist.binaries}/exploded/my-app.jar"/>
</copy>
<jar destfile="${location.dist.binaries}/my-app-unpolluted.jar">
<fileset dir="${location.dist.binaries}/unpolluted/my-app.jar"/>
</jar>
</target>
It's a bit grungy, but it achieves what you want.

How to Setup an ANT file to create a JAVA JAR that is containing/packaging this calculator?

I already made a running program which is calculator in IntelliJ IDEA and make a test and it goes fine. My problem is that how to setup an ANT file to create a JAVA JAR that is calculator.
Hoping for your answers..
tnx.
This is an example of an ant target that does jar creation.
<!-- roll up everyting into a single jar file -->
<target name="dist" depends="clean, compile" description="Generate the distribution file.">
<!--
Copy the library .jars to the directory where the distribution will be located
-->
<copy todir="${dist}">
<fileset dir="${lib}"/>
</copy>
<!-- TODO: Generate the MANIFEST.MF file on the fly -->
<jar jarfile="${dist}/myCalculator.jar" basedir="${build}" manifest="tools/MANIFEST.MF"/>
<!-- dump to web server -->
<copy todir="${web-files}">
<fileset dir="${dist}"/>
</copy>
</target>
Take a look at the Ant manual page for the Jar task. There are lots of examples there.

Creating an ear-File with ant

I am new to ant i referred many sites , i need to build.xml for my project which consists
of two modules i have application.xml file which represents corresponding war file
so my question is it sufficient to add the application.xml file
<ear destfile="${dist.dir}/${ant.project.name}.ear" appxml="${conf.dir}/application.xml">
<metainf dir="${build.dir}/META-INF"/>
<fileset dir="${dist.dir}" includes="*.jar,*.war"/>
</ear>
whether this will refer the corresponding war files or i need to compile the whole scenario please let me know. how solve this.
I'm not 100% sure what you're asking.
In order to use the <ear> task, you already need to have compiled the required jars and wars.
If those jars and wars have already been built, you simply refer to them in your <ear> task as you did in your example. The application.xml must already exist before you build your ear. The application.xml doesn't build the jars and wars, you have to do that.
If you haven't already built the wars and jars, you need to do that first. A general outline of a build.xml looks something like this:
<project name="foo" basedir="." default="package">
<!-- Some standard properties you've defined -->
<property name="target.dir" value="${basedir}/target"/>
<property name="xxx" value="yyy"/>
<property name="xxx" value="yyy"/>
<property name="xxx" value="yyy"/>
<!-- Compile properties that allow overrides -->
<property name="javac.nowarn" value="false"/>
<property name="javac.listfiles" value="false"/>
<property name="javac.srcdir" value="source"/>
<property name="javac.distdir" value="${target.dir}/classes"/>
<target name="clean"
description="cleans everything nice and shiny">
<delete dir="${target.dir}"/>
</target>
<target name="compile"
description="Compiles everything">
<mkdir dir="${javac.distdir}"/>
<javac srcdir="${javac.srcdir}"
destdir="${javac.destdir}"
[...]
[...]/>
</target>
<target name="package.jar"
depends="compile"
description="Package jarfile">
<jar destfile="${target.dir}/jarname.jar"
[...]
[...]/>
</target>
<target name="package.jar2"
depends="compile"
description="Package jarfile">
<jar destfile="${target.dir}/jarname2.jar"
[...]
[...]/>
</target>
<target name="package.war"
depends="compile"
description="Package jarfile">
<war destfile="${target.dir}/jarname.jar"
[...]
[...]/>
</target>
<target name="package"
depends="package.jar"
description="Make the ear">
<ear destfile="${target.dir}/earfile.ear"
[...]/>
</target>
</project>
Basically, it consists of a bunch of targets and each target does one task. You can have targets depend upon other targets. For example, this particular build.xml will automatically run the package task. The package task depends upon the package.jar task which depends upon the compile task. Thus, the build.xml file will first call compile, then package.jar, then package.
The important thing to remember is that you don't specify the order of the events. You let Ant figure that out, and you let Ant figure out what you need to do. Let's say you've modified a java source file. Ant knows that it has to recompile only that one file. It also knows that it might have to rebuild the jarfile that contains that classfile. And, it then knows it has to rebuild the ear. Most tasks can figure it out on their own, and you don't do a clean for each build. (You notice that the clean target isn't called by package or compile. You have to call it manually).
The only other thing I recommend is that you try to keep your work area clean. Any files you create should be put into the ${target.dir} directory. That way, when you do a clean, you only have to delete that one directory.
I hope this answer your question.

How can I specify the path of a JAR in an ant buildfile?

I am executing lot of scp and sshexec and other remote commands from an ant build script. These commands don't work if jsch.jar isn't in the ant lib directory. To make it work, I copied the JAR into the ant lib directory, but this is not a good solution, as anyone else wanting to run the script would have to do the same thing. To run the ant target from Teamcity, we will have to explicitly set the path of the lib file.
Is there a way I can specify the path of the JAR in the ant build XML itself?
Thanks all for your answers. I am managed to get it work with classloader task. This is what I did.
<project basedir="." >
<property environment="env"/>
<taskdef resource="net/jtools/classloadertask/antlib.xml">
<classpath>
<fileset dir="${basedir}/lib" includes="ant-classloader*.jar"/>
</classpath>
</taskdef>
<!--Add JSCH jar to the classpath-->
<classloader loader="system">
<classpath>
<fileset dir="${basedir}/lib" includes="jsch*.jar"/>
</classpath>
</classloader>
<target name="Test">
<scp todir="user1:pass1#server1:/tmp" trust="true" >
<fileset dir="dir1">
<include name="test.txt" />
</fileset>
</scp>
</target>
</project>
As you can see here, I didn't have to give any dependant target for my "Test" target, it just works. It uses classloader, which appends jsch.jar to the system classloader.
One possible work around would be to use the -lib command line option to tell ant where to look for additional jars. Perhaps you could create a wrapper script that calls ant with this option set.
Another way would be to move the ant-jsch.jar file (this is the jar that comes with ant that defines the tasks, not the jsch.jar file you need to download separately) out of your ant lib directory, and create a taskdef for your ssh task separate to the built in one, then set the classpath for this task to the jsch.jar and the ant-jsch.jar:
<taskdef name="sshexec"
classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec">
<classpath>
<pathelement location="jsch-0.1.44.jar"/>
<pathelement location="ant-jsch.jar" />
</classpath>
</taskdef>
I'm not sure this will help you though, since it also involves making changes to the lib directory.
As far as I'm aware, it's not currently possible to specify the extra jars required for the built in tasks in the build file itself in general. There are some special cases, like junit for instance.
To ensure your build is more cross platform I'd suggest using dependency management. The ivy plug-in can automatically install the version of your build's plugin at build-time.
This approach means the last jar you'll ever need to install into your ANT lib is ivy-2.2.0.jar :-)
First declare your project's dependencies in the file ivy.xml
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo"/>
<configurations>
<conf name="anttask" description="Jars implementing ANT tasks"/>
</configurations>
<dependencies>
<dependency org="com.jcraft" name="jsch" rev="0.1.42" conf="anttask->default"/>
</dependencies>
</ivy-module>
Within your build.xml run ivy and use it to populate a custom classpath based on the ivy configuration:
<target name='init' description='Resolve project dependencies and set classpaths'>
<ivy:resolve/>
<ivy:cachepath pathid="anttask.path" conf="anttask"/>
</target>
Finally, elsewhere in your build declare your ANT tasks using the class path now automatically populated by ivy.
<target name='dosomething' depends="init">
<taskdef name="sshexec"
classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec"
classpathref="anttask.path"/>
..
..
</target>
This approach works for all ANT plug-ins, most of which are available in the central Maven repository. The second benefit is that it's easy to upgrade the plug-in versions across all builds.

Resources