Friday, August 03, 2007

JAR is a bitch, and then you die!

[WARNING: This is geeky stuff, if you're NOT a computer geek, then scroll down to the next post. Move along, nothing to see here!]

Instead of the old adage "Life's a bitch, and then you die" I propose a new version: "JAR is a bitch, and then you die".

In case you're living in a bottle or you're not a geek, JAR is the Java JDK tool used to build the compressed ".JAR" (Java Archive) files that contain a Java software application (all the different binary files inside it). It's a tiny command line application just like everything in the JDK that should be no different or more complex to use than ZIP or tar.

However, it proved to be a real pain in the ass and a TIME WASTING EXERCISE, just to update a SINGLE TEXT LINE inside an existing .jar file's "Manifest".

1. What I wanted to do:
Unpack an existing application,
update its MANIFEST file, to include the "Main-Class: ClassnameHere" statement, so I could just start the application by typing

java -jar applicationname.jar
instead of using the author's ancient approach of having to name the "Main-Class" on the command-line every time...

java -classpath applicationname.jar LongNameofTheStupidStartupClass
2. I unpacked the existing application's .jar file just fine with 'unzip', and created a text file with my favorite text editor which read:

Main-Class: LongNameofTheStupidStartupClass

I saved it as "./manifestinfo.txt"

3. So, not having used jar before, I go to a tutorial I find online here

http://java.sun.com/docs/books/tutorial/deployment/jar/modman.html

which reads

"The basic command has this format:

jar cfm jar-file manifest-addition input-file(s)


...and thinking I know what I'm doing, I type:

$ jar -c jftpd.jar ./manifestinfo.txt ./ ./ftpserver/*


I get this as a result:



4. I had to close the terminal as I couldn't type anymore (I could type but I couldn't understand anything).

Then I found what I think is a more friendly explanation on how to update the "Manifest" of the .JAR file with new info, over HERE:
http://java.sun.com/developer/Books/javaprogramming/JAR/basics/update.html

(Later I realize that what happened in Step#3 was that I forgot to include the "f" parameter to indicate that I wanted output to a file rather than to STANDARD OUTPUT. What the **** is the purpose of having output of JAR to standard output?? I don't get it).

5. So I type
jar cf jftpd.jar ftpserver/*
and IT WORKS!
But I forgot about the new manifest info... so I type:

$ jar umf ./manifestinfo.txt ./jftpd.jar
...and I get this beauty:

java.io.IOException: misplaced continuation line
at java.util.jar.Attributes.read(Attributes.java:374)
at java.util.jar.Manifest.read(Manifest.java:182)
at java.util.jar.Manifest.(Manifest.java:52)
at sun.tools.jar.Main.update(Main.java:481)
at sun.tools.jar.Main.run(Main.java:184)
at sun.tools.jar.Main.main(Main.java:1022)
[fcassia@m6810 tmp]$

WHAT THE F#CK IS GOING ON NOW????????

6. You know what was the problem? the file "manifestinfo.txt" had TWO BLANK SPACES before the text "Main-Class: ..." and TWO BLANK LINES below it !! (I pressed [Enter] twice).

Apparently, JAR FREAKS OUT (this is JAVA 6.0 update 2 JDK's JAR, for crying out loud!).

7. I removed the two leading spaces and trailing blank lines and guess what? IT WORKED!.

[fcassia@m6810 tmp]$ jar umf ./manifestinfo.txt ./jftpd.jar
[fcassia@m6810 tmp]$

8. Done.... BASTARDS!!

9. Another Golden Rule when dealing with JAR:
JAR NAME APPARENTLY ALWAYS GOES LAST.

So, contradicting what would be the 'zip' usage of "hey zip, I want to update THIS ZIPFILE with THIS_DATA" (zip something.zip newfile.txt) in JAR is totally the opposite way: "hey JAR, I want THIS NEW FILE into THISJAR" (jar umf newdata ./intothisjar.jar).

GRRR

And finally, found this thread
http://forum.java.sun.com/thread.jspa?threadID=549376

See comment #11:
"
The JAR tool is rather finicky. I have found that each HEADER:VALUE pair must be separated by a newline but not more than 1 newline. When I tried putting blank lines in the MANIFEST, I would get "Error: failed to load Main-Class attribute...". Sun needs to update their tutorials or fix this problem with the JAR tool's manifest parser."

FINICKY? That's being mercyful...

and then comment #15:

"One diference between unix and windows' file system is that text files in unix always have a final linebreak at the end of the file, while windows doesn't need to.

Java, being developed by Sun primarily for the unix comunity requires that the
manifest file ends with a new-line.

no new-line, no manifest file."

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Want more? See the comments on page #2 of the above thread at Sun.com, discussing the "joys" of the "jar" command:

===================================
Does this instruction really work???

http://java.sun.com/docs/books/tutorial/deployment/jar/appman.html

They specify the following command:
jar cmf MyJar.jar Manifest.txt MyPackage/*.class

When I tried that with my classes and files (with different names, of
course), it didn't work. What worked for me is equivalent to the
following:
jar cmf Manifest.txt MyJar.jar MyPackage/*.class

The order is simply permuted. Maybe it's just Apple's Java
implementation. Is this what you were asking about?

-jonnie

==================

The Tutorial is wrong. It should be jar cfm, not cmf.
The order of the command must match the order of the parameters.

==================

Thank you. That is good to know.
===================================

I REST MY CASE:

JAR IS A BITCH, AND THEN YOU DIE

FC

2 comments:

Karim Vaes said...

If you can't type anymore (like you had in the post above), then do a "reset" in your terminal. In the xfce-terminal, this can be done by going to the "Terminal" menu, and then press "Reset".

Fernando (Nerd Gaucho) said...

Thanks for the comment, Karim!. I learn something new about Linux every day, even when I think I know plenty...

I never used the "reset" option and I thought it had to do with sending a conection reset to a Telnet session. :)

So thanks for the heads-up!

FC