Deployed jar can't find the main class while using Apache Ant - java

I built a jar file using the following build.xml below:
<project name="sampleproject">
<!--Source Directory-->
<property name="src.dir" value="src" />
<property name="deploy.name" value="samplejar.jar" />
<property name="lib.dir" value="./lib" />
<property name="deploy.dir" value="target" />
<property name="compile.dir" value="target/classes" />
<!--Define directory name for third party libraries-->
<property name="jar-all" location="${lib.dir}" />
<!--Fileset is a group of files, here **/* matches all jar files across all directory levels-->
<fileset id="jars" dir="${jar-all}">
</fileset>
<!--Setting the classpath-->
<path id="classpath">
<fileset refid="jars" />
<pathelement location="." />
<pathelement location="${src.dir}" />
<pathelement location="${compile.dir}" />
</path>
<path id="cp">
<fileset refid="jars" />
</path>
<pathconvert property="classpath.path" refid="cp" pathsep=" " dirsep="/">
<map from="${jar-all}" to="lib" />
</pathconvert>
<echo>${classpath}</echo>
<!--Clean-->
<target name="clean">
<delete dir="${deploy.dir}" />
</target>
<!--Jar-->
<target name="jar" depends="compile">
<jar destfile="${deploy.dir}/${deploy.name}.jar" basedir="${compile.dir}">
<manifest>
<attribute name="Main-Class" value="main.java.Main" />
<attribute name="Class-Path" value="${classpath.path}" />
</manifest>
<include name="${src.dir}/META-INF/persistence.xml" />
<restrict>
<not>
<or>
<name name="**/*.RSA" />
<name name="**/*.SF" />
<name name="**/*.DSA" />
</or>
</not>
<archives>
<zips>
<fileset dir="lib" includes="**/*.jar" />
</zips>
</archives>
</restrict>
</jar>
</target>
<!--Compile-->
<target name="compile">
<mkdir dir="${compile.dir}" />
<javac includeantruntime="false" srcdir="${src.dir}" destdir="${compile.dir}">
<classpath refid="classpath" />
</javac>
</target>
</project>
My folder structure is like this:
.
./.settings
./bin
./bin/main
./bin/main/java
./bin/META-INF
./lib
./lib/aws
./lib/hibernate
./src
./src/main
./src/main/java
./src/META-INF
./target
./target/classes
./target/classes/main
./target/classes/main/java
My MANIFEST.MF
Created-By: 1.8.0_45-b14 (Oracle Corporation)
Built-Date: ${TODAY}
Main-Class: main.java.Main
Class-Path: lib/activation.jar lib/aws/aws-java-sdk-1.3.2.jar lib/aws/
httpclient-4.1.1.jar lib/aws/httpcore-4.4.1.jar lib/aws/mail-1.4.3.ja
r lib/aws/stax-1.2.0.jar lib/aws/stax-api-1.0.1.jar lib/commons-beanu
tils-1.8.3.jar lib/commons-codec-1.4.jar lib/commons-configuration-1.
8.jar lib/commons-httpclient-3.0.1.jar lib/commons-io-2.1.jar lib/com
mons-lang-2.4.jar lib/commons-logging-1.1.1.jar lib/hibernate/antlr-2
.7.7.jar lib/hibernate/c3p0-0.9.1.jar lib/hibernate/commons-collectio
ns-3.2.1.jar lib/hibernate/dom4j-1.6.1.jar lib/hibernate/hibernate-c3
p0-4.0.1.Final.jar lib/hibernate/hibernate-commons-annotations-4.0.1.
Final.jar lib/hibernate/hibernate-core-4.0.1.Final.jar lib/hibernate/
hibernate-entitymanager-4.0.1.Final.jar lib/hibernate/hibernate-jpa-2
.0-api-1.0.1.Final.jar lib/hibernate/javassist-3.15.0-GA.jar lib/hibe
rnate/jboss-logging-3.1.0.CR2.jar lib/hibernate/jboss-transaction-api
_1.1_spec-1.0.0.Final.jar lib/jackson-all-1.9.5.jar lib/log4j-1.2.16.
jar lib/mail.jar lib/mongo-2.8.0.jar lib/morphia-0.99.jar lib/mysql-c
onnector-java-5.1.12-bin.jar lib/mysql-connector-java-5.1.25-bin.jar
lib/org.json.jar lib/ymmi-utilities-2.0.jar
Whenever I run the jar produced it gives me, Error: Could not find or load main class main.java.Main.
Also, I want to include lib folder & it's subfolders as well since it contains the libraries required to run the jar.
Before it gave me, No Persistence provider for EntityManager named but I solved that by using <include name="${src.dir}/META-INF/persistence.xml" />. Any where I am might wrong?

Your problem is the <include> element in the jar task.
I executed your script and the Main.class file is not added to the jar.
Replace the
<include
by
<fileset dir="${src.dir}" includes="META-INF/persistence.xml" />
This way it will still add all files located in your ${compile.dir}.
Are you new to java ? Normally you put your source files in src/main/java and resources in src/main/resources.
These folders are the root folder of your classpath meaning: If your project classes would be in package org.myproject, than you would put the Main.java file in src/main/java/org/myproject/Main.java
In your build script you than set the property "src.dir" to "src/main/java".
With all resources (=files which do not need to be compiled but must be available in the jar) in src/main/resources the fileset element in the jar task could just be:
<fileset dir="scr/main/resources"/>
This way all resource files you plan to add to your project will automatically added to your jar.
Why do I tell you this ? If one day you would think of using maven instead of ant, maven expects the sources to be in src/main/java and resources in src/main/resources. (https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html)

Regarding you other dependant jars:
Now you include all jars twice: you add the content of them using the <archives> element within the jar and you define them in the Class-Path attribute of the manifest.mf file.
If you really like to build a single jar containing all dependant files, you don't need to specify the manifest attribute Class-Path.
If you want your jar file containing only your files and deliver the dependant jars separately: remove the <archives> element within the jar task.

Related

Using Ant to build dependencies of the current build target

Say I have a library and a binary target, let's call them MyLib and MyBin,
MyBin depends on MyLib.
I'm trying to create an Ant buildfile for MyBin that first builds MyLib and then includes it in the classpath when building MyBin.
I've tried using Ant tasks as in Building other, dependent projects with Ant .
However, it's not working, and from ant -v I think the MyBin build-deps target is not even building MyLib. Seems like it's confusing MyBin and MyLib properties? I'm not sure how to prevent this though.
I'm dumping only MyBin/build.xml below, but the MyLib is almost identical, except it does not have the build-deps target.
<project name="MyBin" default="main" basedir=".">
<property name="projectName" value="MyBin" />
<property name="src.dir" location="src" />
<property name="build.dir" location="bin" />
<property name="dist.dir" location="dist" />
<property name="dist.lib.dir" location="dist/lib" />
<property name="lib.dir" value="lib" />
<target name="build-deps" depends="init">
<!-- MyLib main target does clean -> build -> jar to dist folder -->
<!-- Its build.xml uses many of the same property values as above -->
<ant antfile="../MyLib/build.xml" target="main"/>
</target>
<path id="classpath">
<fileset dir="${basedir}/">
<include name="../MyLib/dist/**/*.jar" />
</fileset>
</path>
<!-- Need classpath to run this -->
<target name="compile" depends="build-deps" description="compile the source ">
<javac includeantruntime="false" srcdir="${src.dir}"
destdir="${build.dir}" classpathref="classpath" />
</target>
<!-- Group all dependencies into a big dependency-all.jar -->
<target name="copy-dependencies">
<mkdir dir="${dist.lib.dir}" />
<jar jarfile="${dist.lib.dir}/dependencies-all.jar">
<zipgroupfileset dir="${lib.dir}">
<include name="**/*.jar" />
</zipgroupfileset>
</jar>
</target>
<!-- jar it, extract above dependency-all.jar and zip it with project files -->
<target name="jar" depends="compile, copy-dependencies"
description="package, output to JAR">
<mkdir dir="${dist.dir}" />
<mkdir dir="${dist.lib.dir}" />
<jar jarfile="${dist.dir}/${projectName}.jar" basedir="${build.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}" />
</manifest>
<zipfileset src="${dist.lib.dir}/dependencies-all.jar"
excludes="META-INF/*.SF" />
</jar>
</target>
<target name="clean" description="clean up">
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>
<!-- Default, run this -->
<target name="main" depends="clean, compile, jar" />
</project>
What I see with ant -v in MyBin is something along the lines of:
build-deps:
Project base dir set to: /MyBin
[ant] calling target(s) [main] in build file /MyLib/build.xml
parsing buildfile /MyLib/build.xml with URI = file:/MyLib/build.xml
Project base dir set to: /MyBin
Override ignored for property "projectName"
Override ignored for property "build.dir"
Override ignored for property "dist.dir"
Override ignored for property "dist.lib.dir"
Override ignored for property "lib.dir"
[pathconvert] Set property classpath.name =
[ant] Entering /MyLib/build.xml...
Build sequence for target(s) `main' is [clean, init, copy-dependencies, jar, main]
Complete build sequence is [clean, init, copy-dependencies, jar, main, ]
clean:
[delete] Deleting directory /MyBin/bin
[delete] Deleting directory /MyBin/bin
init:
[mkdir] Created dir: /MyBin/bin
copy-dependencies:
[ant] Exiting /MyLib/build.xml.
On your specific question:
seems like it's confusing MyBin and MyLib properties? I'm not sure how
to prevent this though.
You are using this to invoke the MyLib build:
<ant antfile="../MyLib/build.xml" target="main" />
A build invoked via the <ant> task this way by default inherits all properties from the caller, including basedir, hence the build is run in the wrong place.
Instead you could use, for example:
<ant dir="../MyLib" />
That will run build.xml in the specified directory, set the basedir property, and call the default target, which should be main if you are using a very similar buildfile for the library as you say. If you don't want to inherit properties from MyBin when executing the MyLib task, specify inheritAll=false in the task.
From the <ant> task docs for the dir attribute:
the directory to use as a basedir for the new Ant project (unless
useNativeBasedir is set to true). Defaults to the current project's
basedir, unless inheritall has been set to false, in which case it
doesn't have a default value. This will override the basedir setting
of the called project. Also serves as the directory to resolve the
antfile and output attribute's values (if any).

java.util.logger.Logging: keeping logger.properties file outside the jar file

In my project I have a properties file which I use to set the level of logging. Now when I export my project as a jar and use it to run the project on a remote machine (linux), I cannot set the level. Is there a way to keep the properties file outside the jar file such that I can set the level and make the jar read that properties file. (preferred using environment variable)
There are several ways to achieve this, for example:
Configure your IDE to export resources outside the JAR: usually I don't consider this option since the specific solution depends by the developer's IDE
Use a generic build tool, for example Ant, and specify in the build.xml file which properties files should be packaged outside the jar
Integrate your project with Maven and customize the package goal in order to copy some specific properties file outside jar
From your question I guess you are exporting the JAR from your IDE, but as I stated above the solution depends by the IDE. For this reason, in order to adopt an IDE independent solution, I would suggest to use Ant. This would allow you to solve this and many similar issues that could arise in the future.
You can get Ant here: just download and unpackage it in any folder, it takes a couple of minutes. Then add a reference to Ant bin directory in your PATH variable (not strictly necessary but suggested) and create a sample build.xml file. Here it is a template example:
<project name="template" default="compile" basedir=".">
<description>Build file template</description>
<property name="project.name" value="myProject"/>
<property name="driver.log" value="log4j-1.2.15.jar"/>
<property name="driver.database" value="ojdbc6.jar"/>
<property name="library.home" value="lib"/>
<property name="env.type" value="dev"/>
<property name="src.version" value="Demo" />
<property name="src.folder" value="root/folder/template"/>
<property name="src.package" value="root.folder.template"/>
<property name="src.home" value="${basedir}/src/${src.folder}"/>
<property name="dist.home" value="${basedir}/dist"/>
<property name="build.home" value="${basedir}/build"/>
<property name="docs.home" value="${basedir}/docs"/>
<!-- Setting the classpath necessary to compile -->
<path id="compile.classpath">
<pathelement location="${library.home}/${driver.log}"/>
<pathelement location="${library.home}/${driver.database}"/>
</path>
<!-- DELETE the class files from the ${build.home} directory tree -->
<target name="clean" description="Clean up the build folder">
<delete dir="${build.home}"/>
<delete dir="${dist.home}"/>
</target>
<!-- CREATE the build directory structure used by compile -->
<target name="init" description="Creates the necessary directories">
<mkdir dir="${dist.home}"/>
<mkdir dir="${build.home}"/>
</target>
<!-- COMPILE the project and copy all necessary resources -->
<!-- Options: <compilerarg value="-Xlint"/> -->
<target name="compile" depends="init" description="Compile the sources">
<javac srcdir="${src.home}" destdir="${build.home}" includeantruntime="false">
<classpath refid="compile.classpath"/>
</javac>
<copy todir="${build.home}/${src.folder}/resources">
<fileset dir="${src.home}/resources">
<include name="messages_list.properties"/>
<include name="messages_list_en.properties"/>
</fileset>
</copy>
<copy file="${src.home}/resources/log4j_${env.type}.properties" tofile="${build.home}/${src.folder}/resources/log4j_${project.name}.properties"/>
<copy file="${src.home}/resources/configuration_${env.type}.properties" tofile="${build.home}/${src.folder}/resources/${project.name}_config.properties"/>
</target>
<!-- Creates the DISTRIBUTABLE JAR package and add 3d part libraries -->
<target name="dist" description="Create the distributable JAR archive">
<jar destfile="${dist.home}/${project.name}.jar">
<fileset dir="${build.home}">
<exclude name="place_holder\"/>
</fileset>
<!-- Setting MANIFEST properties -->
<manifest>
<section name="${ant.project.name} - ver. ${src.version}">
<attribute name="Built_By" value="${user.name}"/>
<attribute name="Created" value="${ts}"/>
</section>
<attribute name="Main-Class" value="package.mine.MainClass"/>
<attribute name="Class-Path" value=". lib/${driver.log} lib/${driver.database}"/>
</manifest>
</jar>
<!-- Adding third part libraries -->
<mkdir dir="${dist.home}/lib"/>
<copy file="${library.home}/${driver.database}" todir="${dist.home}/lib"/>
<copy file="${library.home}/${driver.log}" todir="${dist.home}/lib"/>
</target>
<tstamp><format property="ts" pattern="dd/MM/yyyy HH:mm:ss" /></tstamp>
</project>
Remark: in the template above you should replace the sample JARS (log4j and the OJDBC driver) with the actual JARS needed by your project. Then you can customize the copy task in order to place the properties files where you wish. You can copy those file in any directory you like, as long as such path appears in the application's classpath.

Ant task to filter JARs for zip file and manifest

I have an Ant file where I am creating a zip file and a manifest for several JAR files. Both the zip and the manifest reference the same libraries, but in slightly different ways. If possible I would like to combine the references to the files instead of explicitly writing them twice and hoping the references in both tasks sync up. Below is an example of what I am currently doing.
<target name="zip" depends="default">
<zip destfile="${dist.dir}/${project.name}_v${project.version}.zip">
<zipfileset prefix="lib" dir="lib/Dom4J" includes="*.jar"/>
<zipfileset prefix="lib" dir="lib/GSON" includes="*.jar"/>
<zipfileset prefix="lib" dir="lib/Guava" includes="*.jar"/>
<!-- ... A bunch more (Note I don't want everything
in the lib directory, just certain subfolders
within the lib directory which are explicitly
listed here like GSON. -->
</zip>
</target>
<target name="createManifest">
<!-- Hard code the classpath by hand and hope
they sync up with the zip task -->
<property name="mfClasspath"
value="dom4j-1.6.1.jar gson-2.1.jar guava-11.0.2.jar" />
<!-- Code to use the mfClasspath when creating the manifest
omitted for brevity -->
</target>
What I would ideally like to have is a fileset of some sort that I could reference in both tasks. Note that the manifest does not contain any folders/paths. The manifest only contains the JAR files found within the directories mentioned in the zip task.
You are right. You can accomplish this with a common fileset shared by both the zip and createManifest tasks. For the zip task, copy the files to a temporary location and then zip them up.
For the createManifest task, use character replacement to remove the folders from the paths. Character-replacement strategies are discussed in "Replacing characters in Ant property." If you have Ant-Contrib, you can simplify the character-replacement algorithm below by using the PropertyRegex Ant task.
<project default="all">
<fileset id="jars" dir=".">
<include name="lib/Dom4J/dom4j-1.6.1.jar" />
<include name="lib/GSON/gson-2.1.jar" />
<include name="lib/Guava/guava-11.0.2.jar" />
</fileset>
<target name="zip">
<copy todir="tmp.dir" flatten="true">
<fileset refid="jars" />
</copy>
<zip destfile="example.zip">
<zipfileset dir="tmp.dir" prefix="lib" />
</zip>
<delete dir="tmp.dir" />
</target>
<target name="createManifest">
<property name="jars.property" refid="jars" />
<echo message="${jars.property}" file="some.tmp.file" />
<loadfile property="mfClasspath" srcFile="some.tmp.file">
<filterchain>
<tokenfilter>
<replaceregex pattern="(?:[^;/]+/)+?([^;/]+\.jar)"
replace="\1" flags="g" />
<replacestring from=";" to=" " />
</tokenfilter>
</filterchain>
</loadfile>
<delete file="some.tmp.file" />
</target>
<target name="all" depends="zip, createManifest">
<echo message="$${jars.property} = "${jars.property}"" />
<echo message="$${mfClasspath} = "${mfClasspath}"" />
</target>
</project>
When I executed the above Ant buildfile, the following was output to the console:
Buildfile: /workspace/StackOverflow/build.xml
zip:
[zip] Building zip: /workspace/StackOverflow/example.zip
[delete] Deleting directory /workspace/StackOverflow/tmp.dir
createManifest:
[delete] Deleting: /workspace/StackOverflow/some.tmp.file
all:
[echo] ${jars.property} = "lib/Dom4J/dom4j-1.6.1.jar;lib/GSON/gson-2.1.jar;lib/Guava/guava-11.0.2.jar"
[echo] ${mfClasspath} = "dom4j-1.6.1.jar gson-2.1.jar guava-11.0.2.jar"
BUILD SUCCESSFUL
Total time: 675 milliseconds
Also, example.zip contained the following entries:
lib/dom4j-1.6.1.jar
lib/gson-2.1.jar
lib/guava-11.0.2.jar

Convert to jar while retaining classpath

I have a Java program that uses an external library, whose location is specified through the classpath. I would now like to make the Java program into a standalone jar file (so I can use my IDE for other things whilst the program is running).
How do I turn my existing .java file into an executable jar file?
I am able to make a jar file that includes the class file, manifest file, and the jar file of the library (that was specified in the classpath), but that still appears to be wrong because I get class not found errors.
Ant script for you. What you missed was the classpath generation as a string for the jar task.
<target name="all">
<property name="dir" value="yourProjectDir" />
<property name="name" value="$yourProjectName" />
<!-- clean -->
<delete dir="temp/" />
<mkdir dir="temp/bin/" />
<!-- prepare libs -->
<copy todir="temp/libs/"><fileset dir="${dir}/lbs/" /></copy>
<!-- prepare classpath -->
<pathconvert property="classpath" pathsep=" ">
<path><fileset dir="temp/libs/" /></path>
<chainedmapper><flattenmapper /><globmapper from="*" to="libs/*" /></chainedmapper>
</pathconvert>
<!-- compile -->
<javac destdir="temp/bin/" srcdir="${dir}/src/" target="1.6" source="1.6" includeAntRuntime="false">
<classpath>
<pathelement location="temp/bin/" />
<fileset dir="temp/libs/" />
</classpath>
</javac>
<!-- jar -->
<jar destfile="temp/${name}.jar" basedir="temp/bin/">
<manifest>
<attribute name="Main-Class" value="Main" />
<attribute name="Class-Path" value="${classpath}" />
</manifest>
</jar>
<!-- zip jar + libs -->
<zip destfile="${name}-${version}.zip">
<fileset dir="temp" includes="${name}.jar, libs/" />
</zip>
</target>
The following is the steps, how run stand alone App from command prompt.
1. Create a sample java file then save in particular location(like d:\sample\Hello.java.
2. open command prompt compile that java class, then create jar like Hello.jar file
3. then set classpath in Environment file(like D:\sample\Hello.jar ;
4. Now run your java class , it will work(d:sample>java Hello

Ant Script Example

I would like to make a very simple ant script that does 1 thing, which is to bulid a jar file. But when I try to use a very simple example, it fails due to dependancies on jars that my source depends on. So, How you you specify jars that there are jars that need to be in the class path when building an Ant target.
<project name="project" default="default">
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="lib.dir" value="//tomcat/common/lib"/>
<description> description </description>
<!-- =================================
target: default
================================= -->
<target name="default" depends="compile" description="description">
<jar destfile="/path/to/dir/Library.jar">
</jar>
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}"/>
</target>
</project>
Your question isn't entirely clear - I suspect you mean you want to compile your source (with the javac task) and then build a jar file from the results. If that's not the case, I don't see where your source dependencies come into it. If that is the case, then the jar task is irrelevant.
In the javac task, use the classpath attribute to specify other jar dependencies.
Here's an ANT script generated by using the Eclipse Runnable JAR Export Wizard. This is a project that updates stats on a Google Spreadsheet for a small fantasy baseball league with some friends. It gets the stats by scraping ESPN.com player pages.
Class-Path attribute inside the manifest element is used to set the classpath used by the jar. This defaulted "." but I had to add my src path explicitly so that log4j would pick up log4j.properties.
zipfileset elements are external jars used by my source that I wanted to be included with my jar. I suspect this might be what you're looking for.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar" name="Create Runnable Jar for Project cob_fantasy_baseball">
<!--this file was created by Eclipse Runnable JAR Export Wizard-->
<!--ANT 1.7 is required -->
<target name="create_run_jar">
<jar destfile="C:/workspace/cob_fantasy_baseball/cob_fantasy_baseball.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Main-Class" value="com.me.cob_fantasy_baseball.UpdateCobStats"/>
<attribute name="Class-Path" value=".;src/com/me/cob_fantasy_baseball"/>
</manifest>
<fileset dir="C:/workspace/cob_fantasy_baseball/classes"/>
<zipfileset excludes="META-INF/*.SF" src="C:/workspace/gdata/java/lib/gdata-core-1.0.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/workspace/gdata/java/lib/gdata-spreadsheet-2.0.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/workspace/jericho-html-2.6/lib/jericho-html-2.6.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/workspace/apache-log4j-1.2.15/log4j-1.2.15.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/workspace/jaf-1.1.1/activation.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/workspace/javamail-1.4.2/mail.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/workspace/javamail-1.4.2/lib/smtp.jar"/>
<fileset dir="C:/workspace/cob_fantasy_baseball/src/com/me/cob_fantasy_baseball"/>
</jar>
</target>
</project>
Also, here's a link to the Ant documentation for the jar task: http://ant.apache.org/manual/Tasks/jar.html
Based on your example you can just put libs inside javac:
<javac srcdir="${src.dir}" destdir="${classes.dir}">
<classpath>
<pathelement location="${lib.dir}/lib1.jar"/>
<pathelement location="${lib.dir}/lib2.jar"/>
</classpath>
</javac>
Here is the ant file we use to build the Timeline opensource project. It is pretty straight forward. It doesn't build a jar, but it does use libraries to minimize JS files.
http://simile-widgets.googlecode.com/svn/timeline/trunk/build.xml
Larry

Categories

Resources