Back to the basics of Java — Part 2: The JAR

Ludvig Westerdahl
Javarevisited
Published in
6 min readAug 30, 2021

In the first part I introduced a simple example explaining how to use the classpath correctly. I will use this example for this part as well, so if you did not create that yet you can copy it from the first part before proceeding if you want to follow along.

JAR

A jar file is simply a type of archive used to package java class files and associated resources for distribution. If we remember from the last article, our file structure was like this.

ClasspathProject/
|
---> src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
|
---> bin/myprogram/
|
---> Main.class
|
---> utils/
|
---> Util.class

Let’s create an executable jar containing the class files. First we navigate to the root directory.

First attempt — the messy way

Then we run this command [1].

> jar --create --file project.jar bin/myprogram/Main.class bin/myprogram/utils/Util.class
  • --create (or -c): create a new archive
  • --file (or -f): filename of the archive
  • file(s): the files to include in the archive

Let’s run it.

> java -jar project.jar
no main manifest attribute, in project.jar

It seems like it cannot find a manifest. So let’s inspect what the contents are. Here are three commands that give you the exact same output so I will show all of them but will from now on use the last one.

> jar --list --file project.jar (alternative 1)
> jar -t -f project.jar (alternative 2)
> jar -tf project.jar (alternative 3)
META-INF/
META-INF/MANIFEST.MF
bin/myprogram/Main.class
bin/myprogram/utils/Util.class
  • --list (or -t): list the contents of a jar
  • --file (or -f): the jar file

It doesn’t look very good. Let’s find out what the manifest contains.

> jar --extract --file project.jar (alternative 1)
> jar -xf project.jar (alternative 2)
  • --extract (or -x): extract the content from a jar

Then print the manifest.

> cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: 13.0.2 (Oracle Corporation)

Is this fixable? Sure, I’ll quickly show the fix. The minimum fix is to update the manifest with a classpath.

> echo "Class-Path: bin/" > manifest.txt
> jar --update --file project.jar --manifest manifest.txt
  • --update (or -u): update a jar file
  • --manifest (or -m): manifest file to include information from

Let’s run it again.

> java -jar project.jar
no main manifest attribute, in project.jar

Okey same problem because it is not an executable jar, there is no main class in the jar manifest. However, we can run it by specifying the jar in the classpath.

> java -cp project.jar myprogram.Main
Here is 1337

Let’s update our manifest file by adding a main class attribute and update the jar.

> echo "Main-Class: myprogram.Main" >> manifest.txt
> jar -u -f project.jar -m manifest.txt
Aug 30, 2021 11:52:26 AM java.util.jar.Attributes read
WARNING: Duplicate name in Manifest: Class-Path.
Ensure that the manifest does not have duplicate entries, and that blank lines separate individual sections in both your manifest and in the META-INF/MANIFEST.MF entry in the jar file.

You can ignore this warning because if we inspect the MANIFEST.MF file as shown above you will see that it looks good.

> jar -xf project.jar
> cat META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: 13.0.2 (Oracle Corporation)
Class-Path: bin/
Main-Class: myprogram.Main

But if you wanted, you could have overridden the manifest.txt file with just one new line containing the main-class attribute and it would be changed in the MANIFEST.MF file in the jar.

Alternatively, you can actually specify the main-class attribute on the command line to update the jar, which is a quick way of changing the attribute.

> jar -u -f project.jar --main-class myprogram.Main
> java -jar project.jar
Here is 1337
  • --main-class (or -e): changes the Main-Class attribute in the manifest

Now we can run it like normally.

> java -jar project.jar
Here is 1337

Okey, so this was messy. Let’s try and simplify things. First, let’s clear the directory of the files to start over.

> rm -rf META-INF/ manifest.txt project.jar

Second attempt —the simpler way

Now I’ll try to make this a bit more succinct.

> jar -c -e myprogram.Main -f project.jar -C bin/ myprogram/Main.class -C bin/ myprogram/utils/Util.class
  • -C: changes directory and includes the provided file (2 argument flag, note the space)

Let’s inspect the content.

> jar -tf project.jar
META-INF/
META-INF/MANIFEST.MF
myprogram/Main.class
myprogram/utils/Util.class

Already here you can see that it looks a lot better, no bin directory anywhere. We should be able to run this directly.

> java -jar project.jar
Here is 1337

Alright, great. However, we can actually simplify the command a little bit.

> jar -cfe project.jar myprogram.Main -C bin/ .
> java -jar project.jar
Here is 1337

Here I specified the directory and a wildcard using the dot (.) which takes everything inside the bin directory.

Side note

The ordering of the arguments here is important. If you for instance did this instead:

> jar -cfe myprogram.Main project.jar -C bin/ .

The command would succeed, but you would end up with a file called myprogram.Main with a main class attribute of project.jar.

> ls
bin lib myprogram.Main src
> java -jar myprogram.Main
Error: Could not find or load main class project.jar
Caused by: java.lang.ClassNotFoundException: project.jar

Library jar

For this part, I’m going to move out Util.class into its own jar file similarly to what I did in the first part of this series when explaining the classpath.

So the new file structure will look like this.

ClasspathProject/
|
---> src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
|
---> bin/myprogram/
|
---> Main.class
|
---> lib/myprogram/
|
---> utils/
|
---> Util.class

And we will run the following commands to generate our jar files.

> jar -cfe project.jar myprogram.Main -C bin/ .
> jar -cf projectutils.jar -C lib/ .

Now if we try and run the project.jar we will obviously get an error because it cannot find the Util class.

> java -jar project.jar
Exception in thread "main" java.lang.NoClassDefFoundError: myprogram/utils/Util
at myprogram.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: myprogram.utils.Util
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more

So how do we run this? If we remember from the classpath article we can specify the jars on the classpath like this.

> java -cp project.jar:projectutils.jar myprogram.Main
Here is 1337

But what if we want to make project.jar an executable jar file so that the command that failed above works? Well, then we need to use the manifest like so.

> echo "Main-Class: myprogram.Main" > manifest.txt
> echo "Class-Path: projectutils.jar" >> manifest.txt
> jar -cfm project.jar manifest.txt -C bin/ .
> java -jar project.jar
Here is 1337

Note that the Class-Path attribute is a relative path to the created jar file.

I hope this learning process-like article has helped your understanding of creating jars. Thanks for reading and please give me a like or comment if there is something incorrectly written or it can be improved in any way!

References

[1] The jar Command
https://docs.oracle.com/en/java/javase/16/docs/specs/man/jar.html

[2] Working with Manifest Files: The Basics
https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html

--

--

Ludvig Westerdahl
Javarevisited

I'm a software engineer with a great passion for both software and finance. Hit me up at: https://linkedin.com/in/ludvigwesterdahl