Running a JAR file without directly calling `java`
Asked Answered
H

3

23

I am deploying a command-line tool that is written in Java that accepts command-line arguments. I have it packaged as a JAR file because it is convenient to have a single file.

The problem is that to run it you must first call java -jar (filename) (args) and that is quite annoying.

The current way I have it is to have a simple bash script that launches it, but this is less than ideal.

Is there anyway (in Linux, Ubuntu Server) to make a JAR file that invokes the Java VM by itself? I've looked for a shebang, but couldn't find one (which of course makes sense since it is compiled code).

This is what I want to do: myprogram.jar arg1 -arg2 instead of this: java -jar myprogram.jar arg1 -arg2

Thanks,
Brian

Hyacinthie answered 3/11, 2009 at 14:53 Comment(1)
None of the answers state a better solution I read somewhere where you basically concatenate a shell command at the top of the jar file. Not elegant but very convenient. mesosphere.com/blog/executable-jarsMarco
O
18

See Documentation/java.txt in the Linux Kernel documentation, which tells you how to configure a system using the binfmt_misc kernel module to run Jar files automatically. However, this is a configuration option you change on a computer, not something you change about the jar file, so it doesn't follow the jar file from system to system.

Oliveolivegreen answered 3/11, 2009 at 14:58 Comment(3)
That seems like a great idea, but I can't afford to install the SDK on all of the production machines. I have no problem doing small changes to the target box though. I'm very pleasantly surprised that Linux implemented that though, very impressive.Hyacinthie
You don't need the full JDK (with the compiler) to make this work. The JRE (which you need on the production machines anyway) should be just fine.Oliveolivegreen
Wow! That is a great tool. Worked very well (sans-SDK). Thanks a lot.Hyacinthie
O
44

The .zip file format (upon which the .jar format is based) seems to be robust in the presence of extra data prepended to the file. Thus if you use the cat command to put a shebang before the zip data in the jar file, and make the file executable, then you can call the jar file like you would call any ordinary shell script.

For example: (Note that the unzip -l command is just to illustrate the point. It doesn't change anything about the .jar and can be omitted when you're actually doing this process.)

[bloom@cat-in-the-hat ~]$ java -jar tex4ht.jar 
   xtpipes (2009-01-27-22:19)
   Command line options: 
     java xtpipes [-trace] [-help] [-m] [-E] [-s script_file] [-S script_map]
                  [-i script_dir] [-o out_file] 
                  [-x...ml2xml_arg...]  (-d in_data | in_file)
     -m        messages printing mode
     -E        error messages into exception calls
     in_data   XML data directly into the command line

[bloom@cat-in-the-hat ~]$ cat header.txt 
#!/usr/bin/java -jar
[bloom@cat-in-the-hat ~]$ cat header.txt tex4ht.jar > tex4ht_exe.jar 
[bloom@cat-in-the-hat ~]$ unzip -l tex4ht_exe.jar
Archive:  tex4ht_exe.jar
warning [tex4ht_exe.jar]:  21 extra bytes at beginning or within zipfile
  (attempting to process anyway)
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2009-07-09 15:48   META-INF/
       42  2009-07-09 15:47   META-INF/MANIFEST.MF
        0  2009-07-09 15:48   ./
        0  2009-07-09 15:48   tex4ht/
     2217  2009-07-09 15:48   tex4ht/DbUtilities.class
     2086  2009-07-09 15:48   tex4ht/GroupMn.class
     6064  2009-07-09 15:48   tex4ht/HtJsml.class
     4176  2009-07-09 15:48   tex4ht/HtSpk.class
     1551  2009-07-09 15:48   tex4ht/JsmlFilter.class
     2001  2009-07-09 15:48   tex4ht/JsmlMathBreak.class
     6172  2009-07-09 15:48   tex4ht/OoFilter.class
     3449  2009-07-09 15:48   tex4ht/OoUtilities.class
     1468  2009-07-09 15:48   tex4ht/OomFilter.class
      346  2009-07-09 15:48   xtpipes.class
        0  2009-07-09 15:48   xtpipes/
     4071  2009-07-09 15:48   xtpipes/FileInfo.class
     6904  2009-07-09 15:48   xtpipes/InputObject.class
    25906  2009-07-09 15:48   xtpipes/Xtpipes.class
     1238  2009-07-09 15:48   xtpipes/Xtpipes$5.class
      713  2009-07-09 15:48   xtpipes/Xtpipes$3.class
     1533  2009-07-09 15:48   xtpipes/Xtpipes$1.class
      709  2009-07-09 15:48   xtpipes/Xtpipes$7.class
     1294  2009-07-09 15:48   xtpipes/XtpipesEntityResolver.class
     1235  2009-07-09 15:48   xtpipes/Xtpipes$6.class
     3367  2009-07-09 15:48   xtpipes/Xtpipes$4.class
      709  2009-07-09 15:48   xtpipes/Xtpipes$8.class
     1136  2009-07-09 15:48   xtpipes/Xtpipes$2.class
      875  2009-07-09 15:48   xtpipes/XtpipesPrintWriter.class
     1562  2009-07-09 15:48   xtpipes/XtpipesUni.class
        0  2009-07-09 15:48   xtpipes/util/
     5720  2009-07-09 15:48   xtpipes/util/ScriptsManager.class
     1377  2009-07-09 15:48   xtpipes/util/ScriptsManagerLH.class
---------                     -------
    87921                     32 files
[bloom@cat-in-the-hat ~]$ chmod +x tex4ht_exe.jar
[bloom@cat-in-the-hat ~]$ ./tex4ht_exe.jar 
   xtpipes (2009-01-27-22:19)
   Command line options: 
     java xtpipes [-trace] [-help] [-m] [-E] [-s script_file] [-S script_map]
                  [-i script_dir] [-o out_file] 
                  [-x...ml2xml_arg...]  (-d in_data | in_file)
     -m        messages printing mode
     -E        error messages into exception calls
     in_data   XML data directly into the command line
Oliveolivegreen answered 4/11, 2009 at 0:56 Comment(12)
This is a very interesting solution, although quite unorthodox.Squib
@Daniel: agreed. the binfmt_misc solution is definitely the cleaner one, but this is a seriously cool hack ;-)Strategist
Wow! Amazing. I feel terrible that I can't accept both solutions. @Joachim is correct though, this is not as kosher as binfmt_misc, so that gets the solution.Hyacinthie
@HalfBrian: fine by me -- I posted both solutions.Oliveolivegreen
Haha, didn't even look at that. Thanks again.Hyacinthie
As it is so helpful, here's a one-liner to simplify creating an executable filename='tex4ht.jar' ; echo '#!/usr/bin/java -jar' | cat - $filename > ${filename%.jar} ; chmod +x ${filename%.jar}Tenne
Actually I think this is the better solution if you want to distribute it. As @KenBloom said in the other answer, "this is a configuration option you change on a computer, not something you change about the jar file, so it doesn't follow the jar file from system to system."Wetzell
This actually isn't a hack at all, and definitely should be the accepted answer. The Linux kernel looks for the shebang (#!) at the beginning of any file it opens for execution, and if it encounters it, executes the remainder of that line with the path of the current file as a parameter.Riboflavin
@tophyr: The "hack" part of this is that it's happy concidence that appending "junk" to the beginning of the .jar file doesn't corrupt it to the point that the java command can't understand it.Oliveolivegreen
I'm not privy to the thoughts of the ZIP format designers but I would imagine that's actually a designed feature. The JAR format is simply a ZIP container around a prescribed directory structure with a few metadata files, and the ZIP format intentionally put its "header" information actually in a footer, part of which is specifically a "data start" address. This was, in my mind, most likely done to allow executable data at the beginning of the file, enabling self-extracting archives.Riboflavin
@tophyr: You're right! It's right there on Wikipedia. Thanks for pointing that out.Oliveolivegreen
Do not do this, such a modified JAR broke one of my SBT builds with Scala as the classes in the JAR were not found even though the JAR was on the classpath. Removing the shebang resolved the issue.Jarad
O
18

See Documentation/java.txt in the Linux Kernel documentation, which tells you how to configure a system using the binfmt_misc kernel module to run Jar files automatically. However, this is a configuration option you change on a computer, not something you change about the jar file, so it doesn't follow the jar file from system to system.

Oliveolivegreen answered 3/11, 2009 at 14:58 Comment(3)
That seems like a great idea, but I can't afford to install the SDK on all of the production machines. I have no problem doing small changes to the target box though. I'm very pleasantly surprised that Linux implemented that though, very impressive.Hyacinthie
You don't need the full JDK (with the compiler) to make this work. The JRE (which you need on the production machines anyway) should be just fine.Oliveolivegreen
Wow! That is a great tool. Worked very well (sans-SDK). Thanks a lot.Hyacinthie
E
4

On debian based distribution, it is possible to install jarwrapper

sudo apt-get install jarwrapper

I think that this is possible to do the same thing on other distributions by installing with the same package name.

Eponym answered 19/1, 2014 at 22:34 Comment(1)
Works great. Thanks.Evolute

© 2022 - 2024 — McMap. All rights reserved.