Monday, November 12, 2007

How to make a Jar File

Update. 15/12/2008 12:12:00 AM. Added trouble shooting section based on my first comment. Updated formatting.

Update. 18/12/2008 10:54:10 AM. Added to the trouble shooting section. Removed the Warning in the section "Run Executable JAR from Explorer (Windows)" - it was wrong!

Jar files provide a way to group (and compress) multiple Java files into one "archive" file. If you write an API made up of hundreds of Java files, it is much easier to distribute and import the API using one jar file than manage directories of Java files. Jar archives can be opened with any archive application like WinZip or WinRar (both for Windows). Rar and zip commands are usually native to Linux distro's without any downloads.

Jar files can also be executed, meaning that a complete Java application can be distributed as a single jar and run from the command line or by double clicking the file in your window manager. This tutorial shows you how.

Index


Continuation characters

In certain code snippets below, I use the continuation character "\". This enables a single command to be spread across multiple lines.

Bash continuation character (I use Bash/Cygwin/Windows). It means I can copy and paste the four lines below into a Bash terminal and have them executed as one command.

jar cf HelloWorld.jar \
  org/bram/helloworld/HelloWorld.class \
  org/bram/helloworld/SecondHelloWorld.class \
  org/bram/helloworld/ThirdHelloWorld.class

This shows the same code using the Windows-DOS continuation character. It means I can copy and paste the four lines below into a DOS command prompt and have them executed as one command.

jar cf HelloWorld.jar ^
  org/bram/helloworld/HelloWorld.class ^
  org/bram/helloworld/SecondHelloWorld.class ^
  org/bram/helloworld/ThirdHelloWorld.class

INDEX


Java Files

Gather your Java source. Here is a simple Hello World.

package org.bram.helloworld;
import javax.swing.JOptionPane;
public class HelloWorld {
  public static void main (String [] args) {
    JOptionPane.showMessageDialog(null, "Hello World!");
  }
}

A quick command line test shows it can be compiled and run ok.

javac org/bram/helloworld/HelloWorld.java
java org.bram.helloworld.HelloWorld

I have made a few copies of this file, so I have a few Java files now.

bash3.2 User rbram on host itvw1846 in dir /cygdrive/c/Temp/rbram
Mon Nov 12 09:13 AM> javac org/bram/helloworld/*.java

bash3.2 User rbram on host itvw1846 in dir /cygdrive/c/Temp/rbram
Mon Nov 12 09:13 AM> ls org/bram/helloworld/
.   HelloWorld.class  SecondHelloWorld.class  ThirdHelloWorld.class
..  HelloWorld.java   SecondHelloWorld.java   ThirdHelloWorld.java

INDEX


How To Use Jar Command

Now that we have the Java source and class files, we should look at the 'jar' command. It is a utility program that can be run from the command line (DOS prompt or bash terminal for example, depending on your OS). Here is how to create a compressed JAR file:

jar cf archive_name.jar files

Let's look at each part of that command line.

  • jar - the command to run the jar utility.
  • cf - "c" indicates that we wish to create a jar file (as opposed to extracting one, for example) and "f" indicates that we are going to specify a file name, i.e. "archive_name.jar".
    • Here is a list of the common options. I triggered the output below by issuing an invalid command to view the usage message.
    • bash3.2 User rbram on host itvw1846 in dir /cygdrive/c/Temp/rbram
        Mon Nov 12 09:02 AM> jar invalid
        Usage: jar {ctxu}[vfm0Mi] [jar-file] [manifest-file] [-C dir] files ...
        Options:
            -c  create new archive
            -t  list table of contents for archive
            -x  extract named (or all) files from archive
            -u  update existing archive
            -v  generate verbose output on standard output
            -f  specify archive file name
            -m  include manifest information from specified manifest file
            -0  store only; use no ZIP compression
            -M  do not create a manifest file for the entries
            -i  generate index information for the specified jar files
            -C  change to the specified directory and include the following file
        If any file is a directory then it is processed recursively.
        The manifest file name and the archive file name needs to be specified
        in the same order the 'm' and 'f' flags are specified.
      
        Example 1: to archive two class files into an archive called classes.jar:
               jar cvf classes.jar Foo.class Bar.class
        Example 2: use an existing manifest file 'mymanifest' and archive all the
                   files in the foo/ directory into 'classes.jar':
               jar cvfm classes.jar mymanifest -C foo/ .
      
  • archive_name.jar - name of the JAR file. This can only be included if you use the 'f' option.
  • files - names of all the files you want to put in the jar file. This could be just one name or a list of multiple names separated by space. Names can use pattern matching characters to match multiple files.

INDEX


Non Executable Examples of the Jar Command

So, I have a Java application consisting of three source files that I wish to distribute:

org/bram/helloworld/HelloWorld.java
org/bram/helloworld/SecondHelloWorld.java
org/bram/helloworld/ThirdHelloWorld.java

I also want to call my JAR file HelloWorld.jar. To make a JAR file with just HelloWorld.java:

jar cf HelloWorld.jar org/bram/helloworld/HelloWorld.java org/bram/helloworld/HelloWorld.class

This creates the following Jar

We can't run this file - it is not executable. All we have done is compress multiple Java files (a source and a class file) into a single artefact that you can distribute and include in other Java applications. For example, if someone wanted the magnificent HelloWorld functionality, they can include HelloWorld.jar in their project's classpath and use it in their own Java files.

import org.bram.helloworld.HelloWorld;

...

// Magnificent HelloWorld popup.
HelloWorld.main();

Here is how you can make a jar with all three files listed separately.

jar cf HelloWorld.jar \
  org/bram/helloworld/HelloWorld.class \
  org/bram/helloworld/SecondHelloWorld.class \
  org/bram/helloworld/ThirdHelloWorld.class

We could do the same thing using a pattern match.

jar cf HelloWorld.jar \
org/bram/helloworld/*.class

Of course, we want to include the source files too.

jar cf HelloWorld.jar \
org/bram/helloworld/*.*

It creates the following jar file.

Note that in this example, the source files are in the same directory as the class files are. This is a 'convenience' so that I have all the files in one place. It is common for publicly released API's to have two jars: one with the class files and a different jar with source files.

INDEX


Create An Executable JAR

In the screen shots above, you may have noticed the manifest files. The manifest file holds information about the jar that Java can use, such as the name of a Java class that should be run when if the jar file is executed. See Other Resources for some more detail about manifest files.

Create a text file that lists the "main" class, which references a Java class with a main method that will be run when the jar is executed. I create a text file called "manifest.txt" with the following contents.

Main-Class: org.bram.helloworld.HelloWorld

Note 1. This file MUST end with a blank line i.e. the text file I created had two lines - the line above plus a blank line.

Note 2. You must specify the fully qualified name of the class i.e. package + Java class name. You do not use the class file extension (.class) or source file extension (.java).

I can run the jar utility with a new option "m" and the name of the manifest file. This is the command I use.

jar cmf manifest.txt \
HelloWorld.jar \
org/bram/helloworld/*.java \
org/bram/helloworld/*.class

This is the result.

INDEX


Create An Executable JAR with Ant

Making jar files with a command line is easy for small projects. What happens when you need to jar an API that does have hundreds of files in different packages? The command line would start to get very large and unwieldly. You could put it into a .bat file (DOS) or .sh (*nix), but you still have the problem with the list of files getting large and hard to maintain.

A much better solution is to use an Ant build file. The advantage is that it lets you specify the contents in terms of directories, so it is much easier to maintain and repeat each time a new build is required.

The following would be saved as build.xml to the same base directory as the "org" folder is stored in.

<?xml version="1.0"?>
<project name="HelloWorld" default="buildJar" basedir=".">
  <target name="buildJar">
    <delete file="${basedir}/HelloWorld.jar" failonerror="false" />
    <jar destfile="${basedir}/HelloWorld.jar" basedir="."
        excludes="**/*.bak manifest.txt build.xml">
      <manifest>
        <attribute name="Built-By" value="${user.name}"/>
        <attribute name="Main-Class" value="org.bram.helloworld.HelloWorld"/>
      </manifest>
    </jar>
  </target>
</project>

To run this, simply run the command "ant" from the same base directory.

INDEX


Run Executable JAR from Command Line

I can run it from the command line like this.

java -jar HelloWorld.jar

Here is the result.

INDEX


Run Executable JAR from Explorer (Windows)

Tested on Windows XP If you find this is different on your version of Windows, email me at robertmarkbram AT gmail dot com and let me know.

Within Windows Explorer, you can double click on a jar file and have it automatically execute the main class. This makes jar files a suitable alternative to exe files - very handy for GUI applications distributed as jar files.

There are two caveats. Firstly, you need to have a JRE (or JDK) installed on the machine that will run the jar. Secondly, an association must be set up that tells Windows that .jar files should be run be run by "javaw.exe", which is part of any JRE or JDK installation.

Open Windows Explorer and select "Tools | Folder Options | File Types". You might see that jars are set up by default when you installed Java, as the below screen shot shows. Click on "Advanced" to check it out further.

Clicking "Advanced" shows you the "Edit File Type" dialog below. Click "Edit" on the "open" action.

This is the dialog showing the "open" action. Note the "Application used to perform action".

For me, that value is as below.

"c:\IBM\SDP70\jdk\bin\javaw.exe" -jar "%1" %*

If you don't see the mapping, click "New" in the "Folder Options" dialog. Type "jar" as the "File Extension" and press "OK". Then you can click "Advanced" on the jar association and set up the "open" action as per the screen shots above.

Double clicking on the file executes it.

INDEX


Quick Trouble Shooting Guide

  • You can open jar files with winzip to see what is inside them.
  • You can have .java and .class files in a jar.
  • You must have a .class file with a main method that is referenced in the manifest in order to create an executable jar.
  • Look for error messages - i.e. an error dialog. If you see a command prompt pop up and disappear too quickly for you to see what was in it, try running the jar from the commandline using "javaw.exe -jar" - then you will see the error message in the command window
  • Failed to load Main-Class manifest attribute. Your manifest file doesn't specify the Main-Class attribute. See the create an executable jar section.
  • java.io.IOException: "invalid header field". Did you write your manifest file in Word or WordPad? This might be caused by illegal characters being in this file: try creating and saving the file in a text editor. Found this suggested in a sun forum post.
  • I run the Jar from the command line and see the output ok, but when I double click on my jar file I see no output - no errors either, but still, no output! I am outputting text using System.out.println("") statements. The reason you can't see anything when you double click on the jar file is because System.out.println("") outputs to a console: you have no console, since you didn't launch the application from one, and a console isn't automatically created for you when you launch a file with a double click.
    If you want to see output by double clicking, you can try a JOptionPane - an easy to use GUI component. Try this: javax.swing.JOptionPane.showMessageDialog(null, "My Message");.
  • Could not find the main class. Program will exit. There are a few possibilities.
    • Check the class name listed in your manifest file.
      • Is the class spelt correctly?
      • Is the package correct?
      • You can use slashes instead of periods e.g. my/test/HelloWorld or my.test.HelloWorld will work, so this isn't an issue.
      • Have you mistakenly ended the class name with .class or .java? If so, remove it.
    • Check your Jar File Types setting (for Windows).
      • Have you mistakenly left out some crucial spaces in the arguments list? Make sure you have spaces as per the red highlighted spaces in the following "C:\Program Files\Java\j2re1.4.2_05\bin\javaw.exe" -jar "%1" %*
  • XXX is not recognized as an internal or external command, operable program or batch file. In short, Java's bin dir isn't on your PATh. See my other blog post on this issue.
    • Are you using JDK 1.4 or lower with spaces in the path? E.g. C:\Program Files\Java\j2re1.4.2_05\bin\javaw.exe might need to be shortened using the DOS short form to this instead: "C:\Progra~1\Java\j2re1.4.2_05\bin\javaw.exe

INDEX


Other Resources

INDEX


3 comments:

RobertMarkBram said...

A few quick pointers to help people trouble shoot.

- you can open jar files with winzip to see what is inside them.
- you can have .java and .class files in a jar.
- you must have a .class file with a main method that is referenced in the manifest in order to create an executable jar.
- look for error messages - i.e. an error dialog. If you see a command prompt pop up and disappear too quickly for you to see what was in it, try running the jar from the commandline using "javaw.exe -jar" - then you will see the error message in the command window

Rob
:)

Prototype said...

yes! thank you for the super tutorial. this is very, very helpful and easy to follow along...

RobertMarkBram said...

Glad I could help you. :)
Interesting Javascript Slideshow tutorial your nick links to, I like that a lot. I did my own, much simpler version for my Mum's site. Might need to upgrade it. :)