a question about creating the JAR file

Z

zyng

Hi:

I am a bit confused here. My code uses a third party JAR file, helper.jar. Now, I have finished compiling my code, with helper.jar on the classpath ofcourse. Now it's time to create the executable JAR(hello.jar) for delivery..

My confusion is do I need to bundle helper.jar into hello.jar? One thought I have is that during compiling, all the code needed from helper.jar has been extracted and combined into the generated *.clss files in build/ directory. So, for creating hello.jar, I only need to bundle all *.class files in build/ directory. But I am not sure that is correct.

Currently, I have to unjar(expand) helper.jar into build/ directory before I jar everything in build/. The result hello.jar works fine. If I skip unjarring helper.jar into build/ directory, no complain for creating hello.jar.But when running it, an exception of no class definition for a class inside helper.jar is thrown.

Thank you very much.
 
L

Lew

zyng said:
I am a bit confused here. My code uses a third party JAR file, helper.jar. Now, I have finished
compiling my code, with helper.jar on the classpath of course. Now it's time to create the
executable JAR(hello.jar) for delivery.

My confusion is do I need to bundle helper.jar into hello.jar?

No, in fact that would be stupid.
One thought I have is that during compiling, all the code needed from helper.jar has been extracted
and combined into the generated *.clss [sic] files in build/ directory.
So, for creating hello.jar, I only need to bundle all *.class files in build/ directory.
But I am not sure that is correct.

It is not.
Currently, I have to unjar(expand) helper.jar into build/ directory before I jar everything in build/.

No, you don't. You didn't read the docs on "jar", did you?
The result hello.jar works fine. If I skip unjarring helper.jar into build/ directory, no complain [sic] for
creating hello.jar.

How are you creating "hello.jar"?

Be specific.
But when running it, an exception of no class definition for a class inside helper.jar is thrown.

Do not paraphrase error messages. Copy and paste them into your post.

How are you running the JAR?

You give such incomplete information!

If you are doing this:

$ java -cp hello.jar your.package.Main

then you need to add the third-party JAR to the "-cp" argument.

I will pretend you are attempting this:

$ java -jar hello.jar

which is the normal way to run a JAR. You should package the JAR with its antecedent JARs
into a delivery vehicle like a ZIP file or some installer package that lets the user unpack the
application ("hello.jar") and its antecedents ("helper.jar") into a controlled directory structure.

Two are common - app and antecedents in the same directory, or antecedents in a "lib/"
subdirectory relative to where the app JAR resides.

So one of these:

somedirectory/ somedirectory/
|| ||
||== hello.jar ||== hello.jar
||== lib/ ||== helper.jar
||== helper.jar

You set up the manifest in the app JAR ("hello.jar") to specify the "Class-Path:" relative to its own
location, so

Class-Path: lib/helper.jar

for the "lib/" scenario.
("java -jar" ignores the classpath command argument and the CLASSPATH envar.)

RTFM.
http://docs.oracle.com/javase/7/docs/technotes/guides/jar/index.html
 
S

Sven Köhler

Am 26.10.2012 22:58, schrieb zyng:
My confusion is do I need to bundle helper.jar into hello.jar? One
thought I have is that during compiling, all the code needed from
helper.jar has been extracted and combined into the generated *.clss
files in build/ directory. So, for creating hello.jar, I only need to
bundle all *.class files in build/ directory. But I am not sure that
is correct.

Currently, I have to unjar(expand) helper.jar into build/ directory
before I jar everything in build/. The result hello.jar works fine.
If I skip unjarring helper.jar into build/ directory, no complain for
creating hello.jar. But when running it, an exception of no class
definition for a class inside helper.jar is thrown.

There are at least two ways of doing this:

1) create hello.jar (for example with ant) and embed a manifest file,
which includes helper.jar in the classpath. The good thing is: inside
the manifest, you can use relative paths. Doring java -jar hello.jar,
these paths will be resolved (AFAIK, relative to hello.jar)

2) unpack helper.jar, and include (almost) all files of helper.jar in
hello.jar. Now it's not always possible to do that, especially if you
have multiple JAR files. Some paths are reserved. For example, if you
create a StAX XML Reader, the API search the classpaths for certain
failes. Each files may contain the names of classes that implement the
StAX API. Now when you merge multiple JAR files, you would have to merge
these files as well. (This is actually a bad example, as you only want
only one StAX implementation anyways, but it illustrates the problem)
What about signed JARs?


Now method (2) has gained popularity. Probably, because "big fat JAR
plugins" for several IDEs exist (at least for Eclipse this is true).
However, I think this method should be avoided for any big application.


Regards,
Sven
 
J

Jan Burse

Sven said:
Now method (2) has gained popularity. Probably, because "big fat JAR
plugins" for several IDEs exist (at least for Eclipse this is true).
However, I think this method should be avoided for any big application.

I agree. If your .jar file is an application (something you
double click on the desktop provided your environment has the
right association for .jar extension), then this comes handy.

But I also see the following advantage of 2): When your .jar file
is an API collection, and you want to allow access to the API of
multiple .jars. Then I doubt method 1) works.

Here is the scenario:

util1.jar: Provides class A
util2.jar: Provides class B
I want to make superutil.jar that provides class A and B
so that I can compile against it, i.e.:

javac -cp superutil.jar ....

I guess javac does not understand the Class-Path: manifest
attribute. Right? So you would need to unpack/pack the two
..jars into one .jar. I guess you can use something along the
fileset in the jar ant task for this purpose, no need to
buy an expensive tool.

Bye
 
J

Jan Burse

Jan said:
I guess javac does not understand the Class-Path: manifest
attribute. Right? So you would need to unpack/pack the two
.jars into one .jar. I guess you can use something along the
fileset in the jar ant task for this purpose, no need to
buy an expensive tool.

Corr.:
Somebody says javac does, since jdk 1.5:

"Everything comes with a reason. Since jdk1.5, both java and javac
recurse the manifest classpath of a jar on the java classpath."

http://j2eeblogger.blogspot.ch/2008/04/will-it-compile.html

Oki, Doki, thats colo. So for after 1.5, with the right basic
tools, Sven Köhlers advice is valid. Although there are still
some reasons for option 2), also from the above blog:

"Conclusion? Well, I don't know what to say here, but this looks
something similiar to the windows DLL hell problem. And it was
called DLL 'Hell' for a reason."

Bye
 
S

Sven Köhler

Am 27.10.2012 17:24, schrieb Jan Burse:
Corr.:
Somebody says javac does, since jdk 1.5:

Even if javac would not support that, then I would still recommend to
keep JARs seperate. If my library needs some 3rdparty library, then I
have to document that, and the user has to put that 3rd party library in
the classpath as well, when compiling. This allows the user to update
the 3rd party library separate from my own. This also avoid duplicates
in the classpath (for example, if two libraries include different
versions of the same library in their JAR files).


Regards,
Sven
 
L

Lew

Sven said:
There are at least two ways of doing this:

1) create hello.jar (for example with ant) and embed a manifest file,
which includes helper.jar in the classpath. The good thing is: inside
the manifest, you can use relative paths. Doring java -jar hello.jar,
these paths will be resolved (AFAIK, relative to hello.jar)

"AFAIK" transforms to "It is thus" when you read the docs.

It would not make any sense to use absolute paths in the manifest.
2) unpack helper.jar, and include (almost) all files of helper.jar in
hello.jar. Now it's not always possible to do that, especially if you
have multiple JAR files. Some paths are reserved. For example, if you
create a StAX XML Reader, the API search the classpaths for certain
failes. Each files may contain the names of classes that implement the
StAX API. Now when you merge multiple JAR files, you would have to merge
these files as well. (This is actually a bad example, as you only want
only one StAX implementation anyways, but it illustrates the problem)

It illustrates _a_ problem; I wouldn't call it _the_ problem.

The problem is that it's a stupid idea.

You destroy the purpose of JARs, you tangle up code from multiple sources
into a single vehicle, you violate copyright and licensing terms, you make it
more difficult to update third-party JARs, and there's absolutely no need for
it.
What about signed JARs?

Now method (2) has gained popularity. Probably, because "big fat JAR
plugins" for several IDEs exist (at least for Eclipse this is true).

That's a result of bundling third-party JAR contents inappropriately?
However, I think this method should be avoided for any big application.

Drop the word "big" and you have good advice.
 
Z

zyng

Thank you for the reply. All you said makes sense and I am following you and I agree with your recommendation of zipping hello.jar, helper.jar etc and the two ways of organizing the files.

However, I still have part of my question unanswered: I have two Eclipse projects(HELPER and HELLO). The project HELLO depends on the project HELPER. Now, I want to create an executable JAR file for HELLO project, in build.xml of HELLO project:

<path id="project.classpath">
<pathelement location="${build.dir}" />

<fileset dir="${helper.lib.dir}">
<include name="helper.jar"/>
</fileset>
</path>
Of course, I built helper.jar using Ant beforehand. So now, compiling HELLO code using Ant goes through. As said previously, the code of HELLO project uses the code in HELPER project. The target of "jar" is bundling everything in build directory.

(Pardon me for sticking to my big fat JAR approach). If I don't expand helper.jar into build directory before running "jar" target, running hello.jar will have java.lang.NoClassDefFoundError: aaa/bbb/ccc/AClassInHelper

To make it work, I need expanding helper.jar into build/ beforehand. I thought during compiling the HELLO project, aaa/bbb/ccc/AClassInHelper from helper.jar has been extracted into the binary code xxx/yyy/zzz/AClassInHelloUsingHelperClass.class

Thank you.
 
L

Lew

zyng said:
Thank you for the reply. All you said makes sense and I am following you and I agree with your
recommendation of zipping hello.jar, helper.jar etc and the two ways of organizing the files.>

However, I still have part of my question unanswered: I have two Eclipse projects(HELPER and HELLO).
The project HELLO depends on the project HELPER. Now, I want to create an executable JAR file for
HELLO project, in build.xml of HELLO project:

<path id="project.classpath">
<pathelement location="${build.dir}" />
<fileset dir="${helper.lib.dir}">
<include name="helper.jar"/>
</fileset>
</path>

Of course, I built helper.jar using Ant beforehand. So now, compiling HELLO code using Ant goes
through. As said previously, the code of HELLO project uses the code in HELPER project. The target of
"jar" is bundling everything in build directory.

I don't suppose you'd consider actually showing us that target instead of giving a vague and
approximate description?
(Pardon me for sticking to my big fat JAR approach). If I don't expand helper.jar into build directory
before running "jar" target, running hello.jar will have java.lang.NoClassDefFoundError:
aaa/bbb/ccc/AClassInHelper

That's because you haven't followed the advice given earlier.
To make it work, I need expanding helper.jar into build/ beforehand. I thought during compiling the
HELLO project, aaa/bbb/ccc/AClassInHelper from helper.jar has been extracted into the binary code
xxx/yyy/zzz/AClassInHelloUsingHelperClass.class

Why would you think that? That's not how Java works.

Java doesn't extract code from a antecedent class into a dependent class. It loads the
antecedent class into the JVM from whatever JAR or other source it comes.

In order for your "Hello" program to work, the antecedent JAR, helper.jar in your case,
must be in the classpath.

Unbundling JARs is the wrong thing to do. We've said this to you already. You don't "need
expanding [sic] helper.jar into build/ beforehand". That's the wrong thing to do.

Don't do that.

Read the documentation indicated, which surely you have not done yet.

Isn't it rather rude to keep asking for help when you haven't explored the advice you've
already received?

I repeat:

You set up the manifest in the app JAR ("hello.jar") to specify the "Class-Path:" relative to its own
location, so

Class-Path: lib/helper.jar

for the "lib/" scenario.
("java -jar" ignores the classpath command argument and the CLASSPATH envar.)

RTFM.
http://docs.oracle.com/javase/7/docs/technotes/guides/jar/index.html

RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM. RTFM.
 
R

Roedy Green

My confusion is do I need to bundle helper.jar into hello.jar?

You could do in that way, but repackingt would be considered the baby
way. Embedding a intact jar in a jar is an unnatural act.

You could use Java Webstart to ensure the user has the latest jars.
See http://mindprod.com/jgloss/javawebstart.html

You could some sort of installer to handle this for you.
see http://mindprod.com/jgloss/installer.html

You could do it bare-chested style with an embedded
Class-Path in the master jar.
see http://mindprod.com/jgloss/jar.html
--
Roedy Green Canadian Mind Products http://mindprod.com
Ironically, even though the Internet was created by the US military
[DARPA (Defense Advanced Research Projects Agency)]
to withstand a nuclear attack, it is almost defenceless against malice
from any of its users
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,732
Latest member
ArronPalin

Latest Threads

Top