Ant with Jar Target - java

I need help, to understand the problem of jar packaging with ant. Here my simple code:
<target name="build" depends="compile,test">
<jar
basedir="${bin}/swing.gui"
destfile="${mod}/swing.gui.jar">
<manifest>
<attribute name="Main-Class" value="swing.main.Main"/>
</manifest>
</jar>
</target>
The little program is modularized and as you can see the module swing.gui should be packaged in a jar file. The problem here, when I try to start the module with:
java --module-path bin/mod --module swing.gui
It does not work. The error message says that there is no MainClass attribute and I should try -m / instead.
When I execute this line on the console:
jar --create --file=bin/mod/swing.gui.jar --main-class=swing.main.Main -C bin/src/swing.gui .
It just works! Is that a bug in ant?

Not a bug, exactly. Ant is just doing what you told it to do.
The Main-Class manifest attribute is not used by module loaders. The --main-class option of jar has nothing to do with manifests; it sets a binary class attribute on the jar’s module-info.class entry.
So, your Ant build file is doing something entirely different from what your jar command is doing.
Until Ant’s <jar> task has direct support for this, the workaround is to call the JDK’s jar command directly. Something like this:
<condition property="exec-suffix" value=".exe" else="">
<os family="windows"/>
</condition>
<property name="jar.tool"
location="${java.home}/bin/jar${exec-suffix}"/>
<exec executable="${jar.tool}" failonerror="true">
<arg value="-u"/>
<arg value="-f"/>
<arg file="${mod}/swing.gui.jar"/>
<arg value="-e"/>
<arg value="swing.main.Main"/>
</exec>

Related

bad name in value for --add-modules when trying to compile through ant

I am trying to include the javafx.controls module in my project and compile it through ant, rather then use javaFX javac --module-path "..\lib\javafx-sdk-17.0.2\lib" --add-modules javafx.controls App.java
java --module-path "..\lib\javafx-sdk-17.0.2\lib" --add-modules javafx.controls App commands to compile the file(s).
This is how my ant build.xml looks like right now:
<project name="projectName" default="dist">
<property file="build.properties"/>
<property file="${user.home}/build.properties"/>
<path id="run.classpath">
<fileset dir="${dist.dir}">
<include name="${project.name}.jar"/>
</fileset>
</path>
<target name="compile">
<mkdir dir="${build.dir}"/>
<javac srcdir="${src.dir}"
destdir="${build.dir}"
debug="${compile.debug}"
deprecation="${compile.deprecation}"
optimize="${compile.optimize}"
includeantruntime="false">
<classpath>
<fileset dir="${lib.dir}/javafx-sdk-17.0.2/lib">
<include name="**/*.jar" />
</fileset>
</classpath>
<compilerarg line="--add-modules ${lib.dir}/javafx-sdk-17.0.2/lib/javafx.controls"/>
</javac>
</target>
<target name="dist" depends="compile">
<mkdir dir="${dist.dir}"/>
<jar jarfile="${dist.dir}/${project.name}.jar"
basedir="${build.dir}"
manifest="${src.dir}/Manifest.mf">
<zipgroupfileset dir="${lib.dir}/javafx-sdk-17.0.2/lib" includes="**/*.jar"/>
</jar>
</target>
<target name="build" depends="dist">
<java jar="${dist.dir}/${project.name}.jar" fork="true">
</java>
</target>
<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}"/>
</target>
</project>
The error I keep on getting is: error: bad name in value for --add-modules option: 'C:\xampp\htdocs\projectName/lib/javafx-sdk-17.0.2/lib/javafx.controls'. I have looked up the error, but the answers are very vague in my opinion and have not helped me get more knowledge in regards to the error.
What you are doing wrong
You are providing an incorrect value to --add-modules.
You are not providing a string to ant that matches the example command in your question.
In your question, for the example javac command, you have:
--module-path "..\lib\javafx-sdk-17.0.2\lib" --add-modules javafx.controls
but in your ant script you have
--add-modules ${lib.dir}/javafx-sdk-17.0.2/lib/javafx.controls
You have tried to compress the --module-path argument with the --add-modules argument. Those arguments must remain separate and have different values.
ant javac support for the Java Platform Module System
The ant javac task has a few parameters for working with the Java module system, you should use appropriate ones:
modulepath
Use this to reference the directory set containing your JavaFX modules (e.g. the SDK lib directory).
Do not put the JavaFX libraries on the class path, they are modules and belong on the module path.
Specific changes required for your ant script
I am not familiar enough with the ant argument syntax to provide the exact ant XML elements for the correct definitions of the arguments for your case.
If somebody knows the exact values required in ant element syntax, they can edit this answer and put the values here.
Background info
Read the javac man page if you want to understand what the --module-path and --add-modules arguments are, what data should be supplied to them, and the format required for that data.
Also review understanding java 9 modules.
Alternate approach: defining module info
As an alternative to adding modules via the --add-modules VM argument, you can instead define a module-info.java file in your application to make it modular and require the modules there.
This will work fine if you aren't depending on non-modular code.
Even if you define a module-info.java file, you still need to set the --module-path for the compiler and runtime so that it can find the module implementations. Build tools such as Maven will do this automatically for defined dependencies.
However, ant isn't aware of build dependencies and needs manual help with parameters to support the module system. You will need to set the module path manually via modulepath arguments to the compiler. Similarly, you will need to set VM arguments for runtime module support if you have a java execution task in ant.
solution
<target name="compile">
<mkdir dir="${build.dir}"/>
<javac srcdir="${src.dir}"
destdir="${build.dir}"
debug="${compile.debug}"
deprecation="${compile.deprecation}"
optimize="${compile.optimize}"
includeantruntime="false">
<classpath>
<fileset dir="${lib.dir}/javafx-sdk-17.0.2/lib">
<include name="**/*.jar" />
</fileset>
</classpath>
<compilerarg line="--module-path ${lib.dir}/javafx-sdk-17.0.2/lib/"/>
<compilerarg line="--add-modules javafx.controls"/>
</javac>
</target>
<target name="build" depends="dist">
<java jar="${dist.dir}/${project.name}.jar" fork="true">
<jvmarg line="--module-path ${lib.dir}/javafx-sdk-17.0.2/lib/"/>
<jvmarg line="--add-modules javafx.controls"/>
</java>
</target>
First use the compilerarg with the line, to indicate what you want to compile during the javac process.
<compilerarg line="--module-path ${lib.dir}/javafx-sdk-17.0.2/lib/"/>
<compilerarg line="--add-modules javafx.controls"/>
You then provide these lines within the java process as well.
<jvmarg line="--module-path ${lib.dir}/javafx-sdk-17.0.2/lib/"/>
<jvmarg line="--add-modules javafx.controls"/>

Include log4j in Ant Java task

My project is as given below.
properties
lib
a.jar
b.jar
c.jar
d.jar
log4j.jar
build.xml
I have a java task which references a jar in the lib folder. It writes the output as per the config in log4j properties.
Now where do i place the log4j.properties & mention it in ant java task.
This is how i run the java task & include jars for my project.
<path id="proj.classpath">
<fileset dir="${basedir}/lib">
<include name="*.jar"/>
</fileset>
</path>
<target name="some-task">
<java classname="my.class.main"
classpathref="proj.classpath">
<arg value="some args"/>
</java>
</target>
In your java class, include this in the main method. Pass the file path of the log4j.properties
PropertyConfigurator.configure(args[0]);
Something like this
<java classname="my.class.main"
classpathref="proj.classpath">
<arg value="log4j.properties path"/>
<arg value="some args"/>
</java>

Could not find or load main class- I cannot run my .jar file created from Ant

I have been struggling with this for two days now. I created a very simple HelloWorld class to test if I can get this working but I was not able to.
I get Error- Could not find or load main class...
It works from Eclipse or run task from the script. But double-clicking .jar or running it from CMD gives me the error. What are some possible reasons for this error? Class-path? environmental variables? directory structure? Please help!
package com.hellojava;
public class HelloWorld {
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
My build.xml
<project name="TestProject" basedir="." default="main">
<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="main-class" value="com.hellojava.HelloWorld"/>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}"/>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
</target>
<target name="clean-build" depends="clean,jar"/>
<target name="main" depends="clean,run"/>
run:
[java] Hello World!
main:
BUILD SUCCESSFUL
Total time: 5 seconds
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.2
Created-By: 1.7.0_11-b21 (Oracle Corporation)
Main-Class: com.hellojava.HelloWorld
**Edited:
java -jar TestProject.jar works but javaw -jar TestProject.jar does not. However, I solved the problem- see answer I posted.
The usual syntax to run a jar file is java -jar TestPractice.jar.
If this fails, some analysis is required.
Open your jar file with WinZip, and extract to a temporary directory.
Check that HelloWorld.class file is present, and in the correct directory.
Check that there is a META-INF/MANIFEST.MF file, and it contains a line reading
Main-Class: com.hellojava.HelloWorld I don't see anything in your Ant script that would generate this file.
If all that is OK, your program will run.
For more information, take a look at how to create a bundled runnable jar using Ant
Problems Solved!
I had to change the registry to a correct Java version that I am using. It was set to the previous version of Java that was in my comp.
I can run .jar files through cmd, but I cannot double click them
Also, I noticed that javaw -jar file.jar does not work for me while java -jar file.jar works.
I changed the program that opens .jar files by "open with" and select java instead of javaw. This now solves the problem- I do have one more problem though. I wonder why javaw does not work but for now I'm happy this is working :)
I had the same problem. Application worked fine on my PC but on some PCs I get the same error. Try to upadte Java runtime on machines where your app donesn't work. For example I worked with JRE7 and on PC where app was not working there was Java 1.6 installed.

Java: Ant script works like a charm, but Jar throws exceptions

I created my first Ant script and it's working pretty well. It compiles all my java sources, creates a .jar file and runs the program without even any warning.
But when i try to run my .jar from command line i get NoClassDefFoundError exceptions.
So, how to translate this Ant snippet to work from command line?
<property name="main.class" value="de.bfs.radon.omsimulation.OMMainFrame"/>
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>
<target name="run" depends="jar">
<java fork="true" classname="${main.class}">
<classpath>
<path refid="classpath"/>
<path location="${bin.dir}/omsimulation-${version}.jar"/>
</classpath>
</java>
</target>
This is the command line:
# java -classpath lib/ -jar bin/omsimulation-0.4.45-beta3.jar
Throws:
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: com/toedter/calendar/JDateChooser
at de.bfs.radon.omsimulation.OMMainFrame.(OMMainFrame.java:133)
at de.bfs.radon.omsimulation.OMMainFrame$1.run(OMMainFrame.java:106)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
Why does my .jar file not work with the defined classpath? Further down the Ant script:
<target name="jar" depends="manifest">
<mkdir dir="${bin.dir}" />
<jar jarfile="${bin.dir}/omsimulation-${version}.jar" manifest="${src.dir}/omsimulation.manifest" compress="no" basedir="${build.dir}" includes="de/**" />
</target>
<target name="manifest" depends="compile">
<manifestclasspath property="manifest.cp" jarfile="${bin.dir}/omsimulation-${version}.jar">
<classpath refid="classpath" />
</manifestclasspath>
<manifest file="${src.dir}/omsimulation.manifest">
<attribute name="Built-By" value="${author}"/>
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</target>
Again, running the Ant script works fine. I even tried adding the said libraries to my .jar but that only blows up the file size, the problem still persists.
<jar jarfile="${bin.dir}/omsimulation-${version}.jar" manifest="${src.dir}/omsimulation.manifest" compress="no" basedir="${build.dir}"> <!-- includes="de/**" /-->
<fileset dir="${lib.dir}">
<include name="**/*.jar" />
</fileset>
</jar>
Any ideas on this?
Thanks a lot,
donc_oe
SOLVED: Thanks to perception, the unix command line i was looking for is:
# java -cp lib/*:bin/myjarfile.jar my.package.MyMainClass
And for Windows (note the ; semicolon):
# javaw -cp lib/*;bin/myjarfile.jar my.package.MyMainClass
The relevant thing to note from your build script is this:
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>
With that little snippet you have defined a path construct in Ant, which you then refer to in your run task:
<target name="run" depends="jar">
<java fork="true" classname="${main.class}">
<classpath>
<path refid="classpath"/>
<path location="${bin.dir}/omsimulation-${version}.jar"/>
</classpath>
</java>
</target>
This is in effect executing:
java -cp ${lib.dir}/*.jar:${bin.dir}/omsimulation-${version}.jar ${main.class}
Of course, it does so without the squigly lines and the path(s) fully substituted. The main point being that the command you are trying to run yourself is not equivalent at all. When attempting to invoke from the command line you will need to include in the classpath all the necessar JAR's containing your code and all third party libraries. Assuming everything is still bundled in the Ant created folders, something like:
java -cp <full-path-to-lib>/* -jar <full-path-to-bin>/omsimulation-0.4.45-beta3.jar
Or:
java -cp <full-path-to-lib?/*:<full-path-to-bin>/omsimulation-0.4.45-beta3.jar <MainClass>
ClassDefNotFoundException most likely occur when class is found in classpath, but it is loaded in different classloader, or in a wrong path etc.
From the build file, you appears to create jar that include other jars. This may not give a result you want.
Most likely you want a solution described in Easiest way to merge a release into one JAR file. My personal favorite is one-jar.

Problem running a Jar file

I've compiled a java project into a Jar file, and am having issues running it.
When I run:
java -jar myJar.jar
I get the following error
Could not find the main class: myClass
The class file is not in the root directory of the jar so I've tried changing the path of the main class to match the path to the class file and I get the same issue.
Should I be flattening the file structure? if so how do I do this. I'm using Ant to build the Jar file if thats of any use.
UPDATE
Here is the contents of the jar and the relevant Ant sections, I've changed the name of the firm I work for to "org":
META-INF/
META-INF/MANIFEST.MF
dataAccessLayer/
dataAccessLayer/databaseTest.class
org/
org/eventService/
org/eventService/DatabaseObject.class
org/eventService/DatabaseObjectFactory.class
org/eventService/DbEventClientImpl$HearBeatMonitor.class
org/eventService/DbEventClientImpl.class
org/eventService/EmptyQueryListException.class
org/eventService/EventHandlerWorkItem.class
org/eventService/EventProcessor.class
org/eventService/EventTypeEnum.class
org/eventService/EventWorkQueue$MonitorThread.class
org/eventService/EventWorkQueue$PoolWorker.class
org/eventService/EventWorkQueue.class
org/eventService/FailedToLoadDriverException.class
org/eventService/IConnectionFailureListener.class
org/eventService/InvalidEventTypeException.class
org/eventService/JdbcInterfaceConnection.class
org/eventService/NullArgumentException.class
org/eventService/OracleDatabaseObject.class
org/eventService/ProactiveClientEventLogger.class
org/eventService/ProactiveClientEventLoggerException.class
org/eventService/PropertyMap.class
org/eventService/SQLServerDatabaseObject.class
org/eventService/TestHarness.class
org/eventService/Utilities.class
And the ant target:
<target name="compile" depends="init" description="compile the source ">
<javac srcdir="src" destdir="bin" classpathref="project.class.path"/>
</target>
<target name="buildjar" description="build jar file" depends="compile">
<mkdir dir="dist"/>
<jar destfile="dist/myJar.jar" basedir="bin" includes="**/*.class" >
<manifest>
<attribute name="Main-Class" value="org.eventService.ProactiveClientEventLogger"/>
</manifest>
</jar>
</target>
In your manifest file, make sure you have the attribute Main-Class set to the name of the class containing your main() method. For example, if the package-qualified name of the class is my.cool.Class, then, in your JAR manifest, Main-Class should be set to my.cool.Class.
Also made sure that you have a package declaration in your .java files (for example, in Class.java, make sure you have the proper package my.cool; declaration). Also, make sure your directory hierarchy is set up correctly (my.cool.Class should be in $SRC/my/cool/Class.java).
You should specify your main class during jar creation with full path, something like:
<target name="jar" depends="compile">
<delete file="myJar.jar"/>
<delete file="MANIFEST.MF"/>
<manifest file="MANIFEST.MF">
<attribute name="Main-Class" value="my.package.myClass"/>
</manifest>
<jar destfile="myJar.jar"
basedir="."
includes="**/*.class"
manifest="MANIFEST.MF" />
</target>

Categories