I have just 'inherited' a Java-project and not coming from a Java-background I am a little lost at times. Eclipse is used to debug and run the application during development. I have through Eclipse succeeded in creating a .jar-file that 'includes' all the required external jars like Log4J, xmlrpc-server, etc. This big .jar can then be run successfully using:
java -jar myjar.jar
My next step is to automate builds using Ant (version 1.7.1) so I don't have to involve Eclipse to do builds and deployment. This has proven to be a challenge due to my lacking java-knowledge. The root of the project looks like this:
|-> jars (where external jars have been placed) |-> java | |-> bin (where the finished .class / .jars are placed) | |-> src (Where code lives) | |-> ++files like build.xml etc |-> sql (you guessed it; sql! )
My build.xml contains the following:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <project basedir="." default="build" name="Seraph"> <property environment="env"/> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="build.dir" value="bin"/> <property name="src.dir" value="src"/> <property name="lib.dir" value="../jars"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.dir" value="${build.dir}/jar"/> <property name="jar.file" value="${jar.dir}/seraph.jar"/> <property name="manifest.file" value="${jar.dir}/MANIFEST.MF"/> <property name="main.class" value="no.easyconnect.seraph.core.SeraphCore"/> <path id="external.jars"> <fileset dir="${lib.dir}" includes="**/*.jar"/> </path> <path id="project.classpath"> <pathelement location="${src.dir}"/> <path refid="external.jars" /> </path> <target name="init"> <mkdir dir="${build.dir}"/> <mkdir dir="${classes.dir}"/> <mkdir dir="${jar.dir}"/> <copy includeemptydirs="false" todir="${build.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> </target> <target name="clean"> <delete dir="${build.dir}"/> </target> <target name="cleanall" depends="clean"/> <target name="build" depends="init"> <echo message="${ant.project.name}: ${ant.file}"/> <javac debug="true" debuglevel="${debuglevel}" destdir="bin" source="${source}" target="${target}" classpathref="project.classpath"> <src path="${src.dir}"/> </javac> </target> <target name="build-jar" depends="build"> <delete file="${jar.file}" /> <delete file="${manifest.file}" /> <manifest file="${manifest.file}" > <attribute name="built-by" value="${user.name}" /> <attribute name="Main-Class" value="${main.class}" /> </manifest> <jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <fileset dir="${lib.dir}" includes="**/*.jar" /> </jar> </target> </project>
I then run: ant clean build-jar
and a file named seraph.jar is placed in the java/bin/jar-directory. I then try to run this jar using the following command: java -jar bin/jar/seraph.jar
The result is this output at the console:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger at no.easyconnect.seraph.core.SeraphCore.<clinit>(SeraphCore.java:23) Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:307) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) at java.lang.ClassLoader.loadClass(ClassLoader.java:252) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320) ... 1 more Could not find the main class: no.easyconnect.seraph.core.SeraphCore. Program will exit.
I suspect that I have done something amazingly silly in the build.xml-file and have spent the better part of two days trying variations on the configuration, to no avail. Any help on getting this working is greatly appreciated.
Oh, and I'm sorry if I left some crucial information out. This is my first time posting here at SO.
So you can include any jar.
You can right-click on the project, click on export, type 'jar', choose 'Runnable JAR File Export'. There you have the option 'Extract required libraries into generated JAR'.
From your ant buildfile, I assume that what you want is to create a single JAR archive that will contain not only your application classes, but also the contents of other JARs required by your application.
However your build-jar
file is just putting required JARs inside your own JAR; this will not work as explained here (see note).
Try to modify this:
<jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <fileset dir="${lib.dir}" includes="**/*.jar" /> </jar>
to this:
<jar destfile="${jar.file}" basedir="${build.dir}" manifest="${manifest.file}"> <fileset dir="${classes.dir}" includes="**/*.class" /> <zipgroupfileset dir="${lib.dir}" includes="**/*.jar" /> </jar>
More flexible and powerful solutions are the JarJar or One-Jar projects. Have a look into those if the above does not satisfy your requirements.
With the helpful advice from people who have answered here I started digging into One-Jar. After some dead-ends (and some results that were exactly like my previous results I managed to get it working. For other peoples reference I'm listing the build.xml that worked for me.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <project basedir="." default="build" name="<INSERT_PROJECT_NAME_HERE>"> <property environment="env"/> <property name="debuglevel" value="source,lines,vars"/> <property name="target" value="1.6"/> <property name="source" value="1.6"/> <property name="one-jar.dist.dir" value="../onejar"/> <import file="${one-jar.dist.dir}/one-jar-ant-task.xml" optional="true" /> <property name="src.dir" value="src"/> <property name="bin.dir" value="bin"/> <property name="build.dir" value="build"/> <property name="classes.dir" value="${build.dir}/classes"/> <property name="jar.target.dir" value="${build.dir}/jars"/> <property name="external.lib.dir" value="../jars"/> <property name="final.jar" value="${bin.dir}/<INSERT_NAME_OF_FINAL_JAR_HERE>"/> <property name="main.class" value="<INSERT_MAIN_CLASS_HERE>"/> <path id="project.classpath"> <fileset dir="${external.lib.dir}"> <include name="*.jar"/> </fileset> </path> <target name="init"> <mkdir dir="${bin.dir}"/> <mkdir dir="${build.dir}"/> <mkdir dir="${classes.dir}"/> <mkdir dir="${jar.target.dir}"/> <copy includeemptydirs="false" todir="${classes.dir}"> <fileset dir="${src.dir}"> <exclude name="**/*.launch"/> <exclude name="**/*.java"/> </fileset> </copy> </target> <target name="clean"> <delete dir="${build.dir}"/> <delete dir="${bin.dir}"/> </target> <target name="cleanall" depends="clean"/> <target name="build" depends="init"> <echo message="${ant.project.name}: ${ant.file}"/> <javac debug="true" debuglevel="${debuglevel}" destdir="${classes.dir}" source="${source}" target="${target}"> <src path="${src.dir}"/> <classpath refid="project.classpath"/> </javac> </target> <target name="build-jar" depends="build"> <delete file="${final.jar}" /> <one-jar destfile="${final.jar}" onejarmainclass="${main.class}"> <main> <fileset dir="${classes.dir}"/> </main> <lib> <fileset dir="${external.lib.dir}" /> </lib> </one-jar> </target> </project>
I hope someone else can benefit from this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With