How can I use Ant to build a single executable .jar that has dependency .jars in it in a /lib dir in the .jar?
I have a /lib directory in the project root file that contains all the binary dependency .jars.
In java an executable jar has a specially formatted manifest, that includes the following attributes:
Main-Class
Class-Path
In ANT this is easily accomplished as follows with the jar task:
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}" includes="*.class">
<manifest>
<attribute name="Main-Class" value="${main-class}" />
<attribute name="Class-Path" value="${jar-classpath}" />
</manifest>
</jar>
For extra credit one can use the very useful manifestclasspath task to help assemble the correct classpath string.
<manifestclasspath property="jar-classpath" jarfile="${jar.dir}/${ant.project.name}.jar">
<classpath>
<fileset dir="/path/to/lib/dir" includes="*.jar"/>
</classpath>
</manifestclasspath>
I am building the java project using Ant and I am using Ivy for the dependency management. There are two options in ant build for resolving the dependencies during the deployment. One is the lib mode, if this mode is enabled, all the dependent jars will be downloaded into project's WEB-INF/lib folder. Other is manifest mode which does not download any jar. During deployment, MANIFEST.MF (which has the class-paths to all the dependent jars) file will be used for resolving the jars.
Project is running properly when it is in lib mode, but when manifest is enabled, the build is successful but the deployment fails.
Please tell me how to fix this or what could be the reasons for this?
Note: Tomcat server is VWL enabled by default.
Thanks in advance.
Without an example one must speculate what your code looks like. The following example demonstrates the use of the ANT manifestclasspath task to construct a classpath suitable for a jar manifest file. The ivy "retrieve" task populates a relative direcotory containing the jars:
<target name="build" depends="compile">
<ivy:retrieve pattern="${dist.dir}/lib/[artifact].[ext]"/>
<manifestclasspath property="jar.classpath" jarfile="${dist.jar}">
<classpath>
<fileset dir="${dist.dir}/lib" includes="*.jar"/>
</classpath>
</manifestclasspath>
<jar destfile="${dist.jar}" basedir="${build.dir}/classes">
<manifest>
<attribute name="Main-Class" value="${dist.main.class}"/>
<attribute name="Class-Path" value="${jar.classpath}"/>
</manifest>
</jar>
</target>
The following is a detailed example:
How to avoid copying dependencies with Ivy
I need to include some third party jar file to my project jar. I mentioned it in my build.xml and include this to MANIFEST.MF. Now i get thirdparty1.jar thirdparty2.jar file into inside the project jar. But still i can't able to use the jars. Is it need any addition configuration
Here is my build.xml
<manifest>
<attribute name="Class-Path" value="thirdparty1.jar thirdparty2.jar thirdparty3.jar"/>
If i copy the two jar separately it works well. But i don't understand what is the need for copy these separate. How it solve with out copying jar separately.
If the dependency jar is packaged inside the project jar, you need a solution to load it from there. The standard class-path handling in Java won't access jar files located inside other jar files.
See this answer: Classpath including JAR within a JAR. Specifically the One Jar solution: http://one-jar.sourceforge.net/.
It's also possible to use zipgroupfileset for that.given is the sample ant task for that.
<!-- Build JAR file -->
<target name="jar" depends="init-build-dir,compile-main">
<!--creating a temp jar contains all jar -->
<jar jarfile="${project.build.lib.dir}/external-libs.jar">
<zipgroupfileset dir="${project.lib.redist.dir}">
<include name="**/*.jar" />
</zipgroupfileset>
</jar>
<sleep seconds="1" />
<!-- creating main jar with temp jar-->
<jar jarfile="${project.build.lib.dir}/${ant.project.name}.jar" manifest="MANIFEST.MF">
<fileset dir="${project.build.main.classes.dir}" includes="**/*.*" />
<zipfileset src="${project.build.lib.dir}/external-libs.jar">
<exclude name="*" />
</zipfileset>
</jar>
<!--removing temp jar -->
<delete>
<fileset dir="${project.build.lib.dir}">
<include name="external-libs.jar" />
</fileset>
</delete>
</target>
Ok, I have App.jar as a runnable jar of my applicaiton. It is dependent on many more jars, defining the functionality of the applicaiton (such as worldwind.jar providing 3D globe). One way how to make it runnable is to provide all the jars in one folder set the Class-Path of MANIFEST.MF as :
Class-Path: .
I would, however prefer, to merge all jars into one. Is that possible?
Raw answer: as jar es basically a zip file, just open all the jars you want and copy the classes in the destination jar. However, today we usually let tools like maven to take care of dependencies
Yes it is.
I have a quick example here with Ant, if you're using NetBeans: in the build.xml of your project, add this target and modify the naming as you wish.
This will merge all jars in your "dist/lib" folder with the output jar of your project and put them in the "merged" folder.
You can easily adapt this to suit your need.
Then you only need to right-click the build.xml file, "run target", "other targets", click "merged".
<target name="merged" depends="jar">
<property name="name" value="yourMergedJar"/>
<property name="dir" value="merged"/>
<property name="jar" value="${dir}/${name}.jar"/>
<echo message="Packaging ${application.title} into a single JAR at ${jar}"/>
<delete dir="${dir}"/>
<mkdir dir="${dir}"/>
<jar destfile="${dir}/temp.jar" filesetmanifest="skip">
<zipgroupfileset dir="dist" includes="*.jar"/>
<zipgroupfileset dir="dist/lib" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</jar>
<zip destfile="${jar}">
<zipfileset src="${dir}/temp.jar"
excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete file="${dir}/temp.jar"/>
</target>
Edit
As other members say, you can do this in any IDE (with Ant or Maven) or even manually, as jars are basically zip files.
Yes. I use the Maven shade plugin for exactly that.
mvn clean package shade:shade
When I run "clean and build" the .jar file that is being created only runs if the lib folder is at the same folder of the .jar file.
So if I move the jar file to the desktop and leave the lib folder in the dist folder, the jar file will give me an exception.
How can I deal with this problem?
I solved this by creating just one jar file with all libraries inside, adding the following to my build.xml file in NetBeans:
<target name="-post-jar">
<jar jarfile="dist/Combined-dist.jar">
<zipfileset src="${dist.jar}" excludes="META-INF/*" />
<zipfileset src="lib/commons-io-1.4.jar" excludes="META-INF/*" />
<zipfileset src="lib/ninja-utils-3.2.jar" excludes="META-INF/*" />
<zipfileset src="lib/unicorn-1.0.jar" excludes="META-INF/*" />
<manifest>
<attribute name="Main-Class" value="com.example.mypackage.Main"/>
</manifest>
</jar>
</target>
This creates a jar file (Combined-dist.jar) which is the combination of the dist jar and the specified library jars (in this case, commons-io-1.4.jar,ninja-utils-3.2.jar and unicorn-1.0.jar). You have to be sure to specify your Main Class package for the new jar file or it won't run when you try to open it.
If you copy your jars into the source code directory, they will be in your final jar. Nevetheless, I am not sure if this will work 100% of the time.
There is a great post at java-forum that states the following:
Except for a select few circumstances, what works best for me is to
simply merge the files manually. A .jar is basically a .zip with
organized contents, and you can open them in almost any .zip capable
archive program (I just use gnome's standard archiver, File Roller,
and it works great). Backup your jar file and open it in the archiver
of your choice, and do the same for each library jar in the library
directory. Drag and drop the working folders (IE, everything EXCEPT
the META-INF Directory) from each library into your jar's root path
(alongside your META-INF and your app's root package). Now drag the
META-INF/MANIFEST.MF file from your jar to your Desktop or any other
folder. Open it, and erase the Class-Path and X-COMMENT lines. Don't
forget to leave a blank newline at the end of the file! Save the new
manifest file and drag it back to your jar's META-INF directory,
overwriting the old one. Test the jar.
That's really easy to package every dependent library (*.jar) into one single myProject.jar.
Just follow these steps and you will finally pack every dependent library into single jar. If you are using NetBeans then you can follow exactly or else you need to find your build.xml file in project files.
Follow these steps to edit build.xml
1) Click on Files tab on the left side of the project panel in NetBeans.
2) Double click on the build.xml file and add these lines in it just before </project> line
<target name="package-for-store" depends="jar">
<property name="store.jar.name" value="myProject"/>
<property name="store.dir" value="store"/>
<property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>
<echo message="Packaging ${application.title} into a single JAR at ${store.jar}"/>
<delete dir="${store.dir}"/>
<mkdir dir="${store.dir}"/>
<jar destfile="${store.dir}/temp_final.jar" filesetmanifest="skip">
<zipgroupfileset dir="dist" includes="*.jar"/>
<zipgroupfileset dir="dist/lib" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</jar>
<zip destfile="${store.jar}">
<zipfileset src="${store.dir}/temp_final.jar"
excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete file="${store.dir}/temp_final.jar"/>
</target>
3) Change value in second line of the code as per your project name which is
<property name="store.jar.name" value="myProject"/> //<---Just value not name
4) Save it and right click on build.xml and choose Run Target and then Other Targets and finally click on Package-for-store
5) And here you done. Now you can go and check just like dist folder there will be a store folder which will be containing your final complete jar including all of your dependent libraries. Now whenever you want to change / add more libraries or so, just follow step 4.
Picture for step 4
You could use Apache Ant since version 1.7 for build the JAR with the required libraries in only one file. You could have a configuration file as follows:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="buildJar">
<target name="buildJar">
<!-- Name of jar -->
<jar destfile="C:/MyJar.jar" filesetmanifest="mergewithoutmain">
<manifest>
<!-- Your class with the main method -->
<attribute name="Main-Class" value="myPackage.MyClass"/>
<!-- Path in the jar -->
<attribute name="Class-Path" value="."/>
</manifest>
<!-- Dir of compiled class -->
<fileset dir="C:/NetBeansProjects/MyProject/bin"/>
<!-- Include required jars -->
<zipfileset excludes="META-INF/*.SF"
src="C:/NetBeansProjects/MyProject/lib/library1.jar"/>
<zipfileset excludes="META-INF/*.SF"
src="C:/NetBeansProjects/MyProject/lib/library2.jar"/>
</jar>
</target>
</project>
In Netbeans, place the XML file in your project and run it with the context menu.
See more in Apache Ant User Manual.
If you are going to distribute your app to another pc
You just zip .jar along with lib folder.
If want to run your app from any place in your pc
Take in cosideration Maven way of doing this - create local repository eg. C:\libs where your libraries would exist and .jar could accesses them later from any place in your pc.
Or you could just use Maven. There is a discussion on distributing application with all dependencies (libraries): Java: How do I build standalone distributions of Maven-based projects?.
Copy that jar file to:
C:\Program Files\Java\jdk\jre\lib\ext
and
C:\Program Files\Java\jre\lib\ext
You should be able to use it in Netbeans and in your manual imports, just like standard imports.
I have found maybe the easiest solution for this problem here. You just need to copy the next code snippet at the end of the build.xml file in your project folder.
<target name="-post-jar">
<!-- Change the value to the name of the final jar without .jar -->
<property name="store.jar.name" value="MyJarName"/>
<!-- don't edit below this line -->
<property name="store.dir" value="dist"/>
<property name="temp.dir" value="temp"/>
<property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>
<echo message="Packaging ${application.title} into a single JAR at ${store.jar}"/>
<delete dir="${temp.dir}"/>
<mkdir dir="${temp.dir}"/>
<jar destfile="${temp.dir}/temp_final.jar" filesetmanifest="skip">
<zipgroupfileset dir="dist" includes="*.jar"/>
<zipgroupfileset dir="dist/lib" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</jar>
<delete dir="${store.dir}"/>
<zip destfile="${store.jar}">
<zipfileset src="${temp.dir}/temp_final.jar"
excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete dir="${temp.dir}"/>
</target>
Go to the build.xml in the root of your project and add the code right before </project> tag at the end.
Now change the value of the first propertiy field as commented and save the changes.
From now on, each time you Clean & Build your project, the full jar with dependencies will be generated in the project dist folder
This is what worked for me:
I built in excel export functionality into my project. The 2 external .jars I used for this purpose was jxl.jar end sx.jar
Unpack these 2 jars into a folder(java classes) using 7-Zip without any META files. Unpack your project jar into the same folder including the META file.
Re-Pack the whole java classes folder using JARMaker to recreate your Project .jar in its original distribution folder ... and there you go ... full excel functionality.
user1016588's solution works for me. There's one typo: this line should be
zipfileset src="dist/lib/commons-io-1.4.jar" excludes="META-INF/*"
Try this - in the Netbeans IDE:
Go to Tools --> Libraries
In the dialog box, on the bottom left click "New Library", give a name
On the right side, click on "Add JAR/Folder"
Click OK on the bottom right
Re-start the IDE and check.
Follow these:-
1. Right click on project opened in netbeans editor
2. select properties
3. choose libraries
4. add jar
5. click ok
You can also use this (when the libraries are not in "dist/lib"), tested with NetBeans and ant-contrib:
<target name="-post-jar">
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<!-- Path to ant-contrib -->
<pathelement location="../../Libs/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<!-- Change the value to the name of the final jar without .jar -->
<property name="store.jar.name" value="${application.title}"/>
<!-- don't edit below this line -->
<property name="store.dir" value="dist"/>
<property name="temp.dir" value="temp"/>
<property name="temp.libs.dir" value="temp/libs"/>
<property name="store.jar" value="${store.dir}/${store.jar.name}.jar"/>
<echo message="Packaging ${application.title} into a single JAR at ${store.jar}"/>
<fileset id="store.jars.absolute" dir="${store.dir}" includes="*.jar"/>
<pathconvert property="store.jars.relative" refid="store.jars.absolute" dirsep="/">
<map from="${basedir}/" to=""/>
</pathconvert>
<for list="${store.jars.relative}" param="item">
<sequential>
<echo message="Adding #{item} into single JAR at ${store.jar}"/>
</sequential>
</for>
<delete dir="${temp.dir}"/>
<mkdir dir="${temp.dir}"/>
<for list="${javac.classpath}" param="item" delimiter=":">
<sequential>
<echo message="Adding #{item} into single JAR at ${store.jar}"/>
<copy file="#{item}" todir="${temp.libs.dir}" overwrite="true" />
</sequential>
</for>
<jar destfile="${temp.dir}/temp_final.jar" filesetmanifest="skip">
<zipgroupfileset dir="${store.dir}" includes="*.jar"/>
<zipgroupfileset dir="${temp.libs.dir}" includes="*.*"/>
<manifest>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</jar>
<delete dir="${store.dir}"/>
<zip destfile="${store.jar}">
<zipfileset src="${temp.dir}/temp_final.jar" excludes="META-INF/*.SF, META-INF/*.DSA, META-INF/*.RSA"/>
</zip>
<delete dir="${temp.dir}"/>
</target>