what is correct internal structure of JAR file - java

This is a really silly question that I can't fine a difinitive answer to.
Background.
I'm using Eclipse (with one of the ANT plugins, on an XP terminal).
I have just started playing with ANT, in the [jar] directive I am setting the location of my finished JAR file and I get the following when I 'unzip' the file
META-INF/MANIFEST.MF
MyMainFile.class
which is consistent with that found on the oracle web site for the internal structure.
(here http://docs.oracle.com/javase/tutorial/deployment/jar/view.html )
But when I try to run my file I get a 'main class not found' error ?
I have seen some other posts where people have 'unzipped' the JAR file and ended up with a structure of the following...
META-INF/MANIFEST.MF
dtvcontrol/DTVControlApp.class
(from here http://www.coderanch.com/t/528312/java/java/standalone-application)
So should I get a structure where my class files are in a directory that reflects the name of the package... eg
META-INF/MANIFEST.MF
MyPackage/MyMainFile.class
and if so, why am I getting the incorrect structure using ANT, or why are there 2 different 'correct' internal structures? (how to specifify main-class and classpath for each / control what I get)
Also in case you are interested, in the MANIFEST file states (build using ANT)
[attribute name="Main-Class" value="MyPackage.MyMainFile"/]
Also the directory structure of the package under development is as follows...
/JavaDev/MyTestPackage/src (contains the source files)
//JavaDev/MyTestPackage/bin (contains the class files from eclipse, or from the ANT JAVAC task, have I named it incorrectly? should I have called it build ? )
Further to this, when I create the jar I am not naming it 'MyTestPackage.jar' but simply 'test.jar' could this be causing a problem? I assume not as if I have well understood that is what the [main-class] definition stuff is all about.
Further to all this...
'MyTestPackage' is actualy a small visual error messaging library that I use elsewhere, and I have a second file that has a main class to use for testing. As such it points to various libraries (do I need to copy all the SWT libraries to a specified directory?)
I have read somewhere that if I load libraries into my main class (which I obviously do to open the window) then trying to run the program will fail on a 'main class not found' if I use them, same is also true for adding in any 'static final' members (which I use for the loggin of errors).
'Static Final' problem...
I tried to adjust the classpath in ANT, and I get a load of other errors for the connection to Log4J and my 'wrapper' that I use (to do with it being a bad import), but the libraries exist where they should as set in the classpath).
I feel like I am almost there.... but not quite...
I'm doing this for the small 'library projects' that I am creating with the intention of using MAVAN for the main outer package that will connect them all together... for now however I just want to get this going so as it works.
I can supply the full source, or any other info as required.
Thanks in advance...
David

It's simple when you know where to look. Say your META-INF/MANIFEST.MF contains the line:
Main-Class: mypackage.MyMainFile
then the structure of the jar needs to be
META-INF/MANIFEST.MF
mypackage/MyMainFile.class
where MyMainFile has to be in the proper package:
package mypackage;
public class MyMainFile {
public static void main(String[] args) {
...
Your error message is caused by MyMainFile being in the wrong place.
Edit: it's been a while since the last time i did that with ant, but i think you need something like this: a source file structure that reflects the package struture, say
src/main/java/mypackage/MyMainFile.java
and a directory to put the compiled class file into, say
target
(I'm using maven conventions here, ant doesn't care and you can use the (rightclick)->properties->Java Build path->Sources tab in eclipse to set the source dir to src/main/java and the target to target/classes). Then in ant, have a compile target that compiles from source to target:
<target name="compile">
<mkdir dir="target/classes"/>
<javac srcdir="src/main/java" destdir="target/classes"/>
</target>
so that after ant compile you should see the class file in the target
target/classes/mypackage/MyMainFile.class
Then have a ant jar task that packages this:
<target name="jar" depends="compile">
<jar destfile="target/MyJarFile.jar" basedir="target/classes">
<manifest>
<attribute name="Main-Class" value="mypackage.MyMainFile"/>
</manifest>
</jar>
</target>
After saying ant compile jar you should have a file MyJarFile.jar inside target and
java -jar MyJarFile.jar
should run the main method.

Related

OS X app from jar: could not find or load main class when launched

Upon trying to launch an OS X app I created from a jar file, nothing happens. When I then look into the .app contents and run the JavaAppLauncher, the terminal displays "Error: Could not find or load main class franky.GUI".
The steps I took are the following:
1) I created an executable jar file of my project by using eclipse. The package is named "franky", so when I unarchive the jar file again, there is a folder called franky with the java classes (including GUI.class, which is the main class). The external jars are also present in the top level directory.
2) I created a new folder with a build.xml file, and also the structure dist/frankenbot.jar and lib/appbundler-1.0.jar
3) the ant build file is created following this tutorial.
<property environment="env" />
<taskdef name="bundleapp"
classname="com.oracle.appbundler.AppBundlerTask"
classpath="lib/appbundler-1.0.jar" />
<target name="bundle-franky">
<bundleapp outputdirectory="dist"
name="Franky"
displayname="Franky"
identifier="Franky"
mainclassname="franky.GUI">
<runtime dir="${env.JAVA_HOME}" />
<classpath file="dist/frankenbot.jar" />
<option value="-Dapple.laf.useScreenMenuBar=true"/>
</bundleapp>
</target>
4) I would also like the final app to include JRE, so I made a bash_profile file containing the line 'export JAVA_HOME=$(/usr/libexec/java_home)'. Before building, I execute 'source .bash_profile' in the terminal.
5) Finally, I bundle the app using 'ant bundle-franky'.
The build is succesful and the app is created in the 'dist' directory. But as mentioned above, nothing happens when I click it, and running the JavaAppLauncher gives "Error: Could not find or load main class franky.GUI". I have searched a lot, and I also tried different mainclassnames/identifiers and such, just in case; but nothing seems to work. Any help would be greatly appreciated!

custom ant task with additional libraries

i was writing a custom task for ant in java and my idea was that i can give someone the .jar which contains the java files like the classes and the libraries and the build.xml for ant and he can use it.
If i export my java project the .jar (antTask.jar) contains :
a folder for the compiled classes, one for the libraries, meta-inf folder and .classpath .project files
The ant build.xml looks like this:
<?xml version="1.0" ?>
<project name="repair" basedir="." default="repairTask">
<taskdef name="antTask" classpath="antTask.jar" classname="def.RepairTask"/>
<target....
i don't really understand all this classpath stuff, so can someone tell me what i have to add in my build file so it will work only with this .jar file without the java code sources?
right now i am getting an error that ant can't find one of the libraries i use in the java code with this error (but the antTask.jar contains this lib as another .jar):
taskdef A class needed by class def.RepairTask cannot be found: org/apache/commons/...
using the classloader AntClassLoader[C:...\AntTask\antTask.jar]
i am trying for hours but i just can't figure out how i have to edit my build.xml so i just have to point to this single .jar file and it works..
Thank you guys
All a taskdef does is associate a task name to a classfile that contains the code to execute that task. However, in order to find that classfile, you need to tell <taskdef/> where to find the jar that contains it. That's all classpath does is.
You don't have to define a classpath with the <taskdef/> task. Ant by default looks for all jars that contain code for the <taskdef/> tasks in $ANT_HOME/lib. If you copy your jar to that folder, you could simply define that task this way:
<taskdef name="antTask" classname="def.RepairTask"/>
No need for the classpath. However, I actually don't recommend doing that. Instead, I recommend putting that jar file into your project, so other developers can use your project without having to install that task jar into their $ANT_HOME/lib folder:
<taskdef name='antTask' classname="def.RepairTask">
<classpath>
<fileset dir="${basedir}/antlib/antjar"/>
</classpath>
</taskdef>
Now, when a developer checks out the project that requires the optional task jar, that task jar comes with the project, so they can simply do their build.
There are two ways to define tasks. One is to give a task a name, and then tell <taskdef/> what classfile is associated with that jar as you did above. However, you can also define a resource that also will associate task names with their classes. Here's a common way to include the Ant-Contrib ant tasks:
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<fileset dir="${basedir}/antlib/antcontrib"/>
</classpath>
</taskdef>
If I expand the antcontrib jar, I'll see it contains a net/sf/antcontrib/antcontrib.properties1 file inside the jar. That file looks something like this:
...
# Logic tasks
if=net.sf.antcontrib.logic.IfTask
foreach=net.sf.antcontrib.logic.ForEach
throw=net.sf.antcontrib.logic.Throw
trycatch=net.sf.antcontrib.logic.TryCatchTask
switch=net.sf.antcontrib.logic.Switch
outofdate=net.sf.antcontrib.logic.OutOfDate
runtarget=net.sf.antcontrib.logic.RunTargetTask
timestampselector=net.sf.antcontrib.logic.TimestampSelector
antcallback=net.sf.antcontrib.logic.AntCallBack
antfetch=net.sf.antcontrib.logic.AntFetch
assert=net.sf.antcontrib.logic.Assert
relentless=net.sf.antcontrib.logic.Relentless
# Math Tasks
math=net.sf.antcontrib.math.MathTask
...
All it does is define each task with a classfile for that task. I would recommend you do something similar with your custom ant task. This way, if you decide to include other tasks, you can simply modify this one file, and developers won't have to change their <taskdef/> definition in their jars, or add in multiple ones.
By the way, you should make good and sure that your class doesn't clash with another class that someone else may use. You might want to give your classname a full path that includes a unique prefix:
<taskdef name='antTask' classname="com.vegicorp.anttasks.RepairTask">
Assuming you work for VegiCorp...
1 Ant contrib tasks contain two such files. One is XML format and the other is in properties format. I always use the XML format, and that's what your suppose to use when you define Ant Task resources. I used the properties file because it's a simpler format and easier to see what's going on.

Reflection + libraries java

Edit: this is the Project Setup:
IDE: Eclipse:
Project1 : "Server"
src:
com/mainpackage/main.java
libs:
commons-x-0.jar
PluginInterface.jar
all jar in libs-folder are on the buildpath.
Project2 : "PluginInterface"
src:
com/interfaces/plugininterface
Project3 : "Plugin"
src:
com/package/class1.java - (this implements plugininterface)
libs:
library1.jar
PluginInterface.jar
all jar in libs-folder are on the buildpath
so when i export the Plugin (Project3) i get a jar like this (excluded PluginInterface.jar from export)
com/
com/package/
com/package/class1.class
com/package/class1.java
libs/
libs/library1.jar
library1.jar looks as follows - it is not written by me:
com/
com/stuff/
com/stuff/libclass.java
com/stuff/libclass.class
now i Want to utilize class1 in the "Server" over the Interface:
ClassLoader loader=URLClassLoader.newInstance(
new URL[]{new URL("file:path/to/plugin.jar")},
ClassLoader.getSystemClassLoader()
);
Class<?> pluginclass = Class.forName("com.package.class1", true, loader);
plugininterface ref = (plugininterface)pluginclass.newInstance();
i can now call methods from class1 using the interface both projects know, because both of them include "PluginInterface.jar" in their buildpath.
THE PROBLEM:
"Server" does not recognize "libclass", because is neither in its path nor did i load the class from the plugin.jar in which the library1 is nested.
how do i access this class if an import as library is not possible at the server?
Thanks for any help!
Edit: just for the sake if someone ever has this Problem again, i'll add the ANT files' build-target that makes it work:
<target name="build">
<javac destdir="bin" includeantruntime="false" source="1.7" target="1.7">
<src path="src"/>
<classpath refid="Plugin.classpath"/>
</javac>
<unzip src="${libs}/library1.jar" dest="bin/">
<patternset>
<include name="**/*.class"/>
</patternset>
</unzip>
<jar destfile="plugin.jar" basedir="bin"></jar>
</target>
Just copy the contents of the Library-jar into the build directory (in my case ./bin/). it then isn't even necessary to feed the libraryclasses to the Classloader, it finds them when loading the Classes use them.
The standard class loader does not support nested jar files. You could programmatically extract the jar, or write your own classloader which will decompress the nested files on demand. However, you'd be swimming against the current: such packaging is just not recommended. Instead it is recommended to explode the nested jar into its parent. This is, for example, what Maven dependency plugin does, and the default way to publish a Clojure application with Leiningen.
To achieve your goal from Eclipse the best approach seems be this:
have Eclipse's Export JAR wizard save the ant build scripts it internally generates to build your JAR;
adapt the resulting script to meet your specific needs;
in the future don't run the wizard anymore, but the ant script.
As mentioned by Marko, your standard class loader will not scan through nested jars and nested jars within them. However, if you're willing to play around with TrueZip, you can easily do this without having to extract archives or anything. What's better is that you can have nested archives within nested archives as deep as as you like. So your path could look like:
/path/to/foo.jar/bar/foo/my.zip/containing/some.tar/com/foo/My.class
If you feel comfortable writing your own classloader using TrueZip, this would be a neat way to do it. If not, then you'd have to write a utility class that parses the path and extracts the archives first, before feeding it into the standard URLClassloader.

Need Help in the Creation of Jar File through Java Command?

I need to create jar file of my java app. For that first i had created a file called Manifest.MF , in that file i had stored the following code
Manifest-Version: 1.0
Main-Class: com.demo.test.JavaTest
and then i had exceuted the following command in command prompt
jar cvfm JavaAppDemo.jar MANIFEST.MF "C:\JavaSamples\MyApp"
where MyApp is my project directory, there i had created packages and used other jar files too
and then i try to run the jar using java command
java -jar JavaAppDemo.jar
and i got following Exception
Exception in thread "main"
java.lang.NoClassDefFoundError:
com/demo/test/JavaTest
Caused by:
java.lang.ClassNotFoundException:
com.demo.test.JavaSamp
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native
Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
Could not find the main class:
com.demo.test.JavaTest. Program will
exit.
pls tell me, i want to create a jar and i have to bundle the files in my project directory to that jar files, when i extracted the jar i got my project folder, but when i run my jar i got the above exception so i got confused where the loop hole is present.
You should probably use this syntax for building your jar :
jar cvfm myapp.jar myapp.MF -C classes myclasspath
Okay well a few things:
Here is how you should run your application:
java -classpath myapplication.jar:.. -Dlog4j.info -Dlog4j.configuration=file:../../conf/log4j.xml com.mywebsite.Benchmarks
Take that log4j stuff out if you don't need it. I am sure you are just missing the "com.mywebsite.Benchmarks" line which tells the jar which compiled class to start with. If you have Benchmarks, FullTest, MainApp all compiled in your jar and the main method is in Benchmarks, you need to tell the jar that.
As far as building your jar...
It is the easiest to use some kind of ant build script.
<target name="build">
<delete file="MyApplication.jar" />
<delete dir="_classes" />
<mkdir dir="_classes" />
<javac srcdir="src" destdir="_classes" debug="true">
<classpath path="../../3rdParty/log4j/log4j-1.2.16.jar" />
</javac>
<jar jarfile="MyApplication.jar" basedir="_classes"/>
</target>
That should get you started. But you need to make sure of a few things when your run it:
You have all the dependencies on your class path.
You tell the jar which class to start with.
Some things I can think of:
The error message says that it can't find the file com/demo/test/JavaTest.class. You could try looking at your jar file with a zip file program (a .jar file is compressed just like a .zip file) to see if that file is really not in there. I suspect the error code is actually correct in that it is not actually there, but at least you can see it (or not see it) with your own eyes. And remember that it has to begin with a "com" folder.
Perhaps you are confusing .class files and .java files. Are you sure your .java files are actually compiled into .class files? This might be unlikely, but it may as well be said.
Maybe you need to run your jar command with "C:\JavaSamples\MyApp\*" at the end to pick out all the files in the right directory. Maybe you were telling the jar command to include the folder "MyApp". You don't actually want that folder; you want all the files inside it.

Java code for compiling and executing Java programs

I want to compile and execute a set of java programs one after the other.
Please help me out by giving me some example program on how to carry out the task.
Please advise...
Try using Ant
The documentation should answer your questions.
The HelloWorld app can be found here
Can't you call the main() method of the other programs inside your main method?
Example:
ClassB {
public static void main(String[] args) {
System.out.println("ClassB main() Called");
}
}
ClassA {
public static void main(String[] args) {
System.out.println("ClassA main() Called");
ClassB.main(args);
}
}
The output will be
ClassA main() Called
ClassB main() Called
You can also use the java compiler API programmatically. http://download-llnw.oracle.com/javase/6/docs/api/javax/tools/package-summary.html
The necessary tools jars are hidden away in the JDK directory.
When I had to do this I create a perl script that I ran. Within the script I used system calls to execute the java programs (one after the other). I was also able to change directory between the programs.
Use something like apache ant (http://ant.apache.org) or maven (http://maven.apache.org)
Runtime.getRuntime().exec("javac.exe");
Pass the .java filename(s) to compile.
Runtime.getRuntime().exec("java.exe");
Pass the class name to execute.
Why not using NetBeans or Eclipse? But once you got familiar with those tools, try compiling the sourcecode with 'javac' and executing the classes with 'java' from command line
See e.g. http://java.sun.com/developer/onlineTraining/tools/netbeans_part1/
or http://download.oracle.com/javase/tutorial/getStarted/cupojava/netbeans.html
You could use ant
For instance, if you have two java files in the directory src
src/Hello.java
src/Hola.java
Using this build file will compile and run them:
<project default="compile">
<!-- compile everything inside the "src" directory -->
<target name="compile">
<javac srcdir="src" destdir="classes" />
</target>
<!-- run the program named "Hello" followed by the program "Hola" -->
<target name="run" depends="compile">
<java classname="Hello" classpath="classes" />
<java classname="Hello" classpath="classes" />
</target>
</project>
Save the content in a build.xml and then type ant or ant run
$ ant run
Buildfile: build.xml
compile:
[javac] Compiling 2 source files to /Users/oscarryz/Oscar/code/languages/Java/useAnt/classes
run:
[java] Hello
[java] Hello
BUILD SUCCESSFUL
Total time: 0 seconds
To install ant in your system installing ant or this
So basically what you need is a build system, similar to the make and MAKEFILE combination for generic programming. A build system is a usually a program that parses the file which describes what actions the build system needs to make to produce executable. But as usual there can be significantly more things happening (you can execute the compiled files, generate documentation etc.).
So in the example above by OscarRyz, the ant project file defines the source directory where the files are and after compilation runs the files in that directory. The javac tag and java tag.
Good thing about ant is that it's also written in java so you really don't need to do anything else. And the documentation for ant has been pretty good, with examples, worth definitely reading.
I don't think it would be worthwhile, especially if you are beginner to start using weird Runtime constructs or tinkering with javac directly (unless you have significant amount of time available, which we usually don't :)).

Categories

Resources